Merge branch 'master' into deprecate-radial

This commit is contained in:
Alexandre Zollinger Chohfi 2021-02-17 09:28:56 -08:00 коммит произвёл GitHub
Родитель 017153ee7f 3b36a594cc
Коммит 6ebfaf560e
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
151 изменённых файлов: 1 добавлений и 11081 удалений

Просмотреть файл

@ -1,27 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace Microsoft.Toolkit.Services.Core
{
/// <summary>
/// AuthenticationResult class, parameters: ResponseErrorDetail(uint), ResponseData(string) and ResponseStatus(AuthenticationResultStatus)
/// </summary>
public class AuthenticationResult
{
/// <summary>
/// Gets or sets the authentication error detail
/// </summary>
public uint ResponseErrorDetail { get; set; }
/// <summary>
/// Gets or sets the authentication result data
/// </summary>
public string ResponseData { get; set; }
/// <summary>
/// Gets or sets the authentication status, could be UserCancel, ErrorHttp and Success.
/// </summary>
public AuthenticationResultStatus ResponseStatus { get; set; }
}
}

Просмотреть файл

@ -1,27 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace Microsoft.Toolkit.Services.Core
{
/// <summary>
/// Contains the status of the authentication operation
/// </summary>
public enum AuthenticationResultStatus
{
/// <summary>
/// The operation succeeded, and the response data is available.
/// </summary>
Success,
/// <summary>
/// The operation was canceled by the user
/// </summary>
UserCancel,
/// <summary>
/// The operation failed because a specific HTTP error was returned, for example 404
/// </summary>
ErrorHttp
}
}

Просмотреть файл

@ -1,91 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
namespace Microsoft.Toolkit.Services
{
/// <summary>
/// Base class for data providers in this library.
/// </summary>
/// <typeparam name="TConfig">Query configuration type for given provider.</typeparam>
public abstract class DataProviderBase<TConfig>
{
/// <summary>
/// Initializes a new instance of the <see cref="DataProviderBase{TConfig}"/> class.
/// </summary>
public DataProviderBase()
{
}
/// <summary>
/// Load data from provider endpoint.
/// </summary>
/// <typeparam name="TSchema">Strong typed object to parse the response items into.</typeparam>
/// <param name="config">Query configuration.</param>
/// <param name="maxRecords">Upper record limit.</param>
/// <param name="pageIndex">The zero-based index of the page that corresponds to the items to retrieve.</param>
/// <param name="parser">Parser to use for results.</param>
/// <returns>Strong typed list of results.</returns>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an async method, so nesting generic types is necessary.")]
public async Task<IEnumerable<TSchema>> LoadDataAsync<TSchema>(TConfig config, int maxRecords, int pageIndex, Parsers.IParser<TSchema> parser)
where TSchema : Parsers.SchemaBase
{
if (config == null)
{
throw new ConfigNullException();
}
if (parser == null)
{
throw new ParserNullException();
}
ValidateConfig(config);
var result = await GetDataAsync(config, maxRecords, pageIndex, parser);
if (result != null)
{
return result
.Take(maxRecords)
.ToList();
}
return Array.Empty<TSchema>();
}
private static HttpClient httpClient;
/// <summary>
/// Gets or sets static instance of HttpClient.
/// </summary>
public static HttpClient HttpClient
{
get { return httpClient ?? (httpClient = new HttpClient()); }
set { httpClient = value; }
}
/// <summary>
/// Derived classes will have to implement this method to return provider data
/// </summary>
/// <param name="config">Configuration to use</param>
/// <param name="maxRecords">Maximum number of records to return</param>
/// <param name="pageIndex">The zero-based index of the page that corresponds to the items to retrieve.</param>
/// <param name="parser">Parser to use</param>
/// <typeparam name="TSchema">Schema defining data returned</typeparam>
/// <returns>List of data</returns>
protected abstract Task<IEnumerable<TSchema>> GetDataAsync<TSchema>(TConfig config, int maxRecords, int pageIndex, Parsers.IParser<TSchema> parser)
where TSchema : Parsers.SchemaBase;
/// <summary>
/// Method provided by derived class to validate specified configuration
/// </summary>
/// <param name="config">Configuration to validate</param>
protected abstract void ValidateConfig(TConfig config);
}
}

Просмотреть файл

@ -1,37 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Microsoft.Toolkit.Services
{
/// <summary>
/// Base class for data providers in this library.
/// </summary>
/// <typeparam name="TConfig">Strong typed query configuration object.</typeparam>
/// <typeparam name="TSchema">Strong typed object to parse the response items into.</typeparam>
public abstract class DataProviderBase<TConfig, TSchema> : DataProviderBase<TConfig>
where TSchema : Parsers.SchemaBase
{
/// <summary>
/// Load data from provider endpoint.
/// </summary>
/// <param name="config">Query configuration.</param>
/// <param name="maxRecords">Upper record limit.</param>
/// <param name="pageIndex">The zero-based index of the page that corresponds to the items to retrieve.</param>
/// <returns>List of strong typed objects.</returns>
public Task<IEnumerable<TSchema>> LoadDataAsync(TConfig config, int maxRecords = 20, int pageIndex = 0)
{
return LoadDataAsync(config, maxRecords, pageIndex, GetDefaultParser(config));
}
/// <summary>
/// Default parser abstract method.
/// </summary>
/// <param name="config">Query configuration object.</param>
/// <returns>Strong typed default parser.</returns>
protected abstract Parsers.IParser<TSchema> GetDefaultParser(TConfig config);
}
}

Просмотреть файл

@ -1,35 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Reflection;
namespace Microsoft.Toolkit.Services
{
/// <summary>
/// This class offers general purpose methods.
/// </summary>
internal static class ExtensionMethods
{
/// <summary>
/// Converts between enumeration value and string value.
/// </summary>
/// <param name="value">Enumeration.</param>
/// <returns>Returns string value.</returns>
private static string GetStringValue(Enum value)
{
string output = null;
Type type = value.GetType();
FieldInfo fi = type.GetRuntimeField(value.ToString());
Parsers.Core.StringValueAttribute[] attrs = fi.GetCustomAttributes(typeof(Parsers.Core.StringValueAttribute), false) as Parsers.Core.StringValueAttribute[];
if (attrs != null && attrs.Length > 0)
{
output = attrs[0].Value;
}
return output;
}
}
}

Просмотреть файл

@ -1,26 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Microsoft.Toolkit.Services.Core
{
/// <summary>
/// This gets an Uri value.
/// </summary>
public interface IAuthenticationBroker
{
/// <summary>
/// Returns the authentication status, it could be UserCancel, ErrorHttp and Success.
/// </summary>
/// <param name="requestUri"> Authorization base url</param>
/// <param name="callbackUri"> LinkedInOAuthTokens callbackUri</param>
/// <returns> Returns a status </returns>
Task<AuthenticationResult> Authenticate(Uri requestUri, Uri callbackUri);
}
}

Просмотреть файл

@ -1,32 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Microsoft.Toolkit.Services
{
/// <summary>
/// Generic interface that all deployed service providers implement.
/// </summary>
/// <typeparam name="T">Reference to underlying data service provider.</typeparam>
/// <typeparam name="U">Strongly-typed schema for data returned in list query.</typeparam>
/// <typeparam name="V">Configuration type specifying query parameters.</typeparam>
public interface IDataService<T, U, V>
{
/// <summary>
/// Gets the underlying data service provider.
/// </summary>
T Provider { get; }
/// <summary>
/// Makes a request for a list of data from the given service provider.
/// </summary>
/// <param name="config">Describes the query on the list data request.</param>
/// <param name="maxRecords">Specifies an upper limit to the number of records returned.</param>
/// <param name="pageIndex">The zero-based index of the page that corresponds to the items to retrieve.</param>
/// <returns>Returns a strongly typed list of results from the service.</returns>
Task<List<U>> RequestAsync(V config, int maxRecords, int pageIndex = 0);
}
}

Просмотреть файл

@ -1,32 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace Microsoft.Toolkit.Services.Core
{
/// <summary>
/// This interface gets a PasswordCredential, store the credential and remove the key.
/// </summary>
public interface IPasswordManager
{
/// <summary>
/// Gets the user credentials.
/// </summary>
/// <param name="key"> Receive the storage key user and the access token </param>
/// <returns> Returns user credential.</returns>
PasswordCredential Get(string key);
/// <summary>
/// Store users credential.
/// </summary>
/// <param name="resource"> Resource</param>
/// <param name="credential"> Username and password.</param>
void Store(string resource, PasswordCredential credential);
/// <summary>
/// Remove users credential.
/// </summary>
/// <param name="key"> Credential unique key</param>
void Remove(string key);
}
}

Просмотреть файл

@ -1,21 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace Microsoft.Toolkit.Services.Core
{
/// <summary>
/// Provides platform specific logic to sign request for OAuth communication
/// </summary>
public interface ISignatureManager
{
/// <summary>
/// Generate request signature
/// </summary>
/// <param name="baseString">String to sign</param>
/// <param name="secret">Secret to use to sign</param>
/// <param name="append">If true append &amp; to the base string</param>
/// <returns>The signed baseString to use in the OAuth requests</returns>
string GetSignature(string baseString, string secret, bool append = false);
}
}

Просмотреть файл

@ -1,29 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Threading.Tasks;
namespace Microsoft.Toolkit.Services.Core
{
/// <summary>
/// This interface store the key value
/// </summary>
public interface IStorageManager
{
/// <summary>
/// Gets the key value
/// </summary>
/// <param name="key"> Token value </param>
/// <returns> Returns a string value</returns>
Task<string> GetAsync(string key);
/// <summary>
/// Sets the key value
/// </summary>
/// <param name="key"> Token key </param>
/// <param name="value"> String value </param>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
Task SetAsync(string key, string value);
}
}

Просмотреть файл

@ -1,22 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace Microsoft.Toolkit.Services.Core
{
/// <summary>
/// PasswordCredential class composed of UserName and Password, both strings.
/// </summary>
public class PasswordCredential
{
/// <summary>
/// Gets or sets the username from login form
/// </summary>
public string UserName { get; set; }
/// <summary>
/// Gets or sets the password from login form
/// </summary>
public string Password { get; set; }
}
}

Просмотреть файл

@ -1,43 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
namespace Microsoft.Toolkit.Services
{
/// <summary>
/// Exception for null Config.
/// </summary>
public class ConfigNullException : Exception
{
/// <summary>
/// Initializes a new instance of the <see cref="ConfigNullException"/> class.
/// Default constructor.
/// </summary>
public ConfigNullException()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ConfigNullException"/> class.
/// Constructor accepting additional message string.
/// </summary>
/// <param name="message">Additional error information.</param>
public ConfigNullException(string message)
: base(message)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ConfigNullException"/> class.
/// Constructor accepting additional message string and inner exception
/// </summary>
/// <param name="message">Additional error information.</param>
/// <param name="innerException">Reference to inner exception.</param>
public ConfigNullException(string message, Exception innerException)
: base(message, innerException)
{
}
}
}

Просмотреть файл

@ -1,43 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
namespace Microsoft.Toolkit.Services
{
/// <summary>
/// Exception for config parameter being null.
/// </summary>
public class ConfigParameterNullException : Exception
{
/// <summary>
/// Initializes a new instance of the <see cref="ConfigParameterNullException"/> class.
/// Default constructor.
/// </summary>
public ConfigParameterNullException()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ConfigParameterNullException"/> class.
/// Accepts parameter name.
/// </summary>
/// <param name="parameter">Name of the parameter.</param>
public ConfigParameterNullException(string parameter)
: base(string.Format("The parameter '{0}' in config is null.", parameter))
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ConfigParameterNullException"/> class.
/// Accepts parameter name and inner exception.
/// </summary>
/// <param name="message">Name of the parameter.</param>
/// <param name="innerException">Reference to the inner exception.</param>
public ConfigParameterNullException(string message, Exception innerException)
: base(message, innerException)
{
}
}
}

Просмотреть файл

@ -1,43 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
namespace Microsoft.Toolkit.Services
{
/// <summary>
/// Exception for no OAuth keys being present.
/// </summary>
public class OAuthKeysNotPresentException : Exception
{
/// <summary>
/// Initializes a new instance of the <see cref="OAuthKeysNotPresentException"/> class.
/// Default constructor.
/// </summary>
public OAuthKeysNotPresentException()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="OAuthKeysNotPresentException"/> class.
/// Constructor with information on missing key.
/// </summary>
/// <param name="key">Name of the missing key.</param>
public OAuthKeysNotPresentException(string key)
: base(string.Format("Open Authentication Key '{0}' not present", key))
{
}
/// <summary>
/// Initializes a new instance of the <see cref="OAuthKeysNotPresentException"/> class.
/// Constructor with additional message and inner exception.
/// </summary>
/// <param name="message">Additional exception message.</param>
/// <param name="innerException">Reference to inner exception.</param>
public OAuthKeysNotPresentException(string message, Exception innerException)
: base(message, innerException)
{
}
}
}

Просмотреть файл

@ -1,43 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
namespace Microsoft.Toolkit.Services
{
/// <summary>
/// Exception for revoked OAuth keys.
/// </summary>
public class OAuthKeysRevokedException : Exception
{
/// <summary>
/// Initializes a new instance of the <see cref="OAuthKeysRevokedException"/> class.
/// Default constructor.
/// </summary>
public OAuthKeysRevokedException()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="OAuthKeysRevokedException"/> class.
/// Constructor with additional message.
/// </summary>
/// <param name="message">Additional message</param>
public OAuthKeysRevokedException(string message)
: base(message)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="OAuthKeysRevokedException"/> class.
/// Constructor with additional message and inner exception.
/// </summary>
/// <param name="message">Additional message.</param>
/// <param name="innerException">Reference to inner exception.</param>
public OAuthKeysRevokedException(string message, Exception innerException)
: base(message, innerException)
{
}
}
}

Просмотреть файл

@ -1,43 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
namespace Microsoft.Toolkit.Services
{
/// <summary>
/// Exception for null Parser.
/// </summary>
public class ParserNullException : Exception
{
/// <summary>
/// Initializes a new instance of the <see cref="ParserNullException"/> class.
/// Default constructor.
/// </summary>
public ParserNullException()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ParserNullException"/> class.
/// Constructor with additional message.
/// </summary>
/// <param name="message">Additional message</param>
public ParserNullException(string message)
: base(message)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ParserNullException"/> class.
/// Constructor with additional message and inner exception.
/// </summary>
/// <param name="message">Additional message.</param>
/// <param name="innerException">Reference to inner exception.</param>
public ParserNullException(string message, Exception innerException)
: base(message, innerException)
{
}
}
}

Просмотреть файл

@ -1,55 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Net;
namespace Microsoft.Toolkit.Services
{
/// <summary>
/// Exception for failed requests.
/// </summary>
public class RequestFailedException : Exception
{
/// <summary>
/// Initializes a new instance of the <see cref="RequestFailedException"/> class.
/// Default constructor.
/// </summary>
public RequestFailedException()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="RequestFailedException"/> class.
/// Constructor with additional message.
/// </summary>
/// <param name="message">Additional message.</param>
public RequestFailedException(string message)
: base(message)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="RequestFailedException"/> class.
/// Constructor with status code and reason for request failure.
/// </summary>
/// <param name="statusCode">Failure status code.</param>
/// <param name="reason">Failure reason.</param>
public RequestFailedException(HttpStatusCode statusCode, string reason)
: base(string.Format("Request failed with status code {0} and reason '{1}'", (int)statusCode, reason))
{
}
/// <summary>
/// Initializes a new instance of the <see cref="RequestFailedException"/> class.
/// Constructor with additional message and inner exception.
/// </summary>
/// <param name="message">Additional message.</param>
/// <param name="innerException">Reference to inner exception.</param>
public RequestFailedException(string message, Exception innerException)
: base(message, innerException)
{
}
}
}

Просмотреть файл

@ -1,43 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
namespace Microsoft.Toolkit.Services
{
/// <summary>
/// Exception for too many requests.
/// </summary>
public class TooManyRequestsException : Exception
{
/// <summary>
/// Initializes a new instance of the <see cref="TooManyRequestsException"/> class.
/// Default constructor.
/// </summary>
public TooManyRequestsException()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="TooManyRequestsException"/> class.
/// Constructor with additional message.
/// </summary>
/// <param name="message">Additional message.</param>
public TooManyRequestsException(string message)
: base(message)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="TooManyRequestsException"/> class.
/// Constructor with additional message and reference to inner exception.
/// </summary>
/// <param name="message">Additional message.</param>
/// <param name="innerException">Reference to inner exception.</param>
public TooManyRequestsException(string message, Exception innerException)
: base(message, innerException)
{
}
}
}

Просмотреть файл

@ -1,43 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
namespace Microsoft.Toolkit.Services
{
/// <summary>
/// Exception for user not found.
/// </summary>
public class UserNotFoundException : Exception
{
/// <summary>
/// Initializes a new instance of the <see cref="UserNotFoundException"/> class.
/// Default constructor.
/// </summary>
public UserNotFoundException()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="UserNotFoundException"/> class.
/// Constructor with screen/user name information.
/// </summary>
/// <param name="screenName">Name of user not found.</param>
public UserNotFoundException(string screenName)
: base("User " + screenName + " not found.")
{
}
/// <summary>
/// Initializes a new instance of the <see cref="UserNotFoundException"/> class.
/// Constructor with screen/user name information and inner exception.
/// </summary>
/// <param name="screenName">Name of user not found.</param>
/// <param name="innerException">Reference to inner exception.</param>
public UserNotFoundException(string screenName, Exception innerException)
: base("User " + screenName + " not found.", innerException)
{
}
}
}

Просмотреть файл

@ -1,53 +0,0 @@
<Project Sdk="MSBuild.Sdk.Extras">
<PropertyGroup>
<TargetFrameworks>uap10.0.17763;netstandard2.0;NET462</TargetFrameworks>
<Title>Windows Community Toolkit .NET Standard Services</Title>
<Description>
This .NET standard library enables access to different data sources such as Microsoft Graph, OneDrive, Twitter, Microsoft Translator, and LinkedIn. It is part of the Windows Community Toolkit.
</Description>
<PackageTags>UWP Community Toolkit Windows Microsoft Graph OneDrive Twitter Translator LinkedIn service login OAuth</PackageTags>
<LangVersion>8.0</LangVersion>
<NoWarn>CS8002;CS0618</NoWarn>
<DeterministicSourcePaths Condition="'$(EnableSourceLink)' == ''">false</DeterministicSourcePaths>
</PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)' == 'uap10.0.17763'">
<DefineConstants Condition="'$(DisableImplicitFrameworkDefines)' != 'true'">$(DefineConstants);WINRT</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)' == 'net462'">
<UseWpf>true</UseWpf>
<EnableDefaultPageItems>false</EnableDefaultPageItems>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Microsoft.Toolkit.Parsers\Microsoft.Toolkit.Parsers.csproj" />
<ProjectReference Include="..\Microsoft.Toolkit\Microsoft.Toolkit.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="System.Text.Json" Version="4.7.2" />
<PackageReference Include="System.Net.Http" Version="4.3.4" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)'=='uap10.0.17763'">
<ProjectReference Include="..\Microsoft.Toolkit.Uwp\Microsoft.Toolkit.Uwp.csproj" />
</ItemGroup>
<ItemGroup Condition="!('$(TargetFramework)'=='net462')">
<Compile Remove="PlatformSpecific\NetFramework\**\*" />
<None Remove="PlatformSpecific\NetFramework\**\*" />
<Page Remove="PlatformSpecific\NetFramework\**\*" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)'=='net462'">
<Reference Include="System.Web" />
<PackageReference Include="Microsoft.Toolkit.Wpf.UI.Controls.WebView" Version="[5.0.0-preview.gb86cb1c4cb,)" />
<PackageReference Include="Microsoft.Toolkit.Forms.UI.Controls.WebView" Version="[5.0.0-preview.gb86cb1c4cb,)" />
</ItemGroup>
<ItemGroup Condition="!('$(TargetFramework)'=='uap10.0.17763')">
<Compile Remove="PlatformSpecific\Uwp\**\*" />
</ItemGroup>
</Project>

Просмотреть файл

@ -1,78 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
#if WINRT
using Windows.Security.Cryptography;
using Windows.Security.Cryptography.Core;
using Windows.Storage.Streams;
#endif
namespace Microsoft.Toolkit.Services.OAuth
{
/// <summary>
/// OAuth Encoder.
/// </summary>
internal static class OAuthEncoder
{
/// <summary>
/// Url encode input string.
/// </summary>
/// <param name="value">Input string.</param>
/// <returns>Encoded string.</returns>
public static string UrlEncode(string value)
{
if (string.IsNullOrEmpty(value))
{
return string.Empty;
}
var result = Uri.EscapeDataString(value);
// UrlEncode escapes with lowercase characters (e.g. %2f) but oAuth needs %2F
result = Regex.Replace(result, "(%[0-9a-f][0-9a-f])", c => c.Value.ToUpper());
// these characters are not escaped by UrlEncode() but needed to be escaped
result = result
.Replace("(", "%28")
.Replace(")", "%29")
.Replace("$", "%24")
.Replace("!", "%21")
.Replace("*", "%2A")
.Replace("'", "%27");
// these characters are escaped by UrlEncode() but will fail if unescaped!
result = result.Replace("%7E", "~");
return result;
}
/// <summary>
/// Encode list of parameters.
/// </summary>
/// <param name="parameters">List of parameters.</param>
/// <returns>Encoded string of parameters.</returns>
public static string UrlEncode(IEnumerable<OAuthParameter> parameters)
{
string rawUrl = string.Join("&", parameters.OrderBy(p => p.Key).Select(p => p.ToString()).ToArray());
return UrlEncode(rawUrl);
}
#if WINRT
public static string GenerateHash(string input, string key)
{
MacAlgorithmProvider mac = MacAlgorithmProvider.OpenAlgorithm(MacAlgorithmNames.HmacSha1);
IBuffer keyMaterial = CryptographicBuffer.ConvertStringToBinary(key, BinaryStringEncoding.Utf8);
CryptographicKey cryptoKey = mac.CreateKey(keyMaterial);
IBuffer hash = CryptographicEngine.Sign(cryptoKey, CryptographicBuffer.ConvertStringToBinary(input, BinaryStringEncoding.Utf8));
return CryptographicBuffer.EncodeToBase64String(hash);
}
#endif
}
}

Просмотреть файл

@ -1,65 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Globalization;
namespace Microsoft.Toolkit.Services.OAuth
{
/// <summary>
/// OAuth parameter.
/// </summary>
internal class OAuthParameter
{
/// <summary>
/// Gets or sets key property.
/// </summary>
public string Key { get; set; }
/// <summary>
/// Gets or sets value property.
/// </summary>
public string Value { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="OAuthParameter"/> class.
/// Constructor accepting key and value.
/// </summary>
/// <param name="key">Key.</param>
/// <param name="value">Value.</param>
public OAuthParameter(string key, string value)
{
Key = key;
Value = value;
}
/// <summary>
/// ToString override.
/// </summary>
/// <returns>String representation</returns>
public override string ToString()
{
return ToString(false);
}
/// <summary>
/// Format key / value into string.
/// </summary>
/// <param name="withQuotes">Whether to create quotes in string.</param>
/// <returns>Formatted string of key / value.</returns>
public string ToString(bool withQuotes)
{
string format;
if (withQuotes)
{
format = "{0}=\"{1}\"";
}
else
{
format = "{0}={1}";
}
return string.Format(CultureInfo.InvariantCulture, format, OAuthEncoder.UrlEncode(Key), OAuthEncoder.UrlEncode(Value));
}
}
}

Просмотреть файл

@ -1,62 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
namespace Microsoft.Toolkit.Services.OAuth
{
/// <summary>
/// OAuth Uri extensions.
/// </summary>
internal static class OAuthUriExtensions
{
/// <summary>
/// Get query parameters from Uri.
/// </summary>
/// <param name="uri">Uri to process.</param>
/// <returns>Dictionary of query parameters.</returns>
public static IDictionary<string, string> GetQueryParams(this Uri uri)
{
var dict = uri.Query.Remove(0, 1).Split('&').ToDictionary(c => c.Split('=')[0], c => Uri.UnescapeDataString(c.Split('=')[1]));
return dict;
}
/// <summary>
/// Get absolute Uri.
/// </summary>
/// <param name="uri">Uri to process.</param>
/// <returns>Uri without query string.</returns>
public static string AbsoluteWithoutQuery(this Uri uri)
{
if (string.IsNullOrEmpty(uri.Query))
{
return uri.AbsoluteUri;
}
return uri.AbsoluteUri.Replace(uri.Query, string.Empty);
}
/// <summary>
/// Normalize the Uri into string.
/// </summary>
/// <param name="uri">Uri to process.</param>
/// <returns>Normalized string.</returns>
public static string Normalize(this Uri uri)
{
var result = new StringBuilder(string.Format(CultureInfo.InvariantCulture, "{0}://{1}", uri.Scheme, uri.Host));
if (!((uri.Scheme == "http" && uri.Port == 80) || (uri.Scheme == "https" && uri.Port == 443)))
{
result.Append(string.Concat(":", uri.Port));
}
result.Append(uri.AbsolutePath);
return result.ToString();
}
}
}

Просмотреть файл

@ -1,81 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Threading.Tasks;
using System.Windows;
using Microsoft.Toolkit.Services.Core;
using ApplicationForm = System.Windows.Forms.Application;
namespace Microsoft.Toolkit.Services.PlatformSpecific.NetFramework
{
internal class NetFrameworkAuthenticationBroker : IAuthenticationBroker
{
public Task<AuthenticationResult> Authenticate(Uri requestUri, Uri callbackUri)
{
int numberForms = ApplicationForm.OpenForms.Count;
if (numberForms > 0)
{
return this.AuthenticateForm(requestUri, callbackUri);
}
else if (Application.Current != null)
{
return this.AuthenticateWindow(requestUri, callbackUri);
}
else
{
// Your code shouldn't reach this exception.
throw new Exception("Cannot identify the current application. Please review your main app");
}
}
public async Task<AuthenticationResult> AuthenticateWindow(Uri requestUri, Uri callbackUri)
{
PopupWPF popupWindow;
var taskCompletionSource = new TaskCompletionSource<AuthenticationResult>();
popupWindow = new PopupWPF(callbackUri);
popupWindow.Closed += (sender, e) =>
{
taskCompletionSource.SetResult(HandleExit(popupWindow.ActualUrl));
};
popupWindow.Show();
popupWindow.NavigateTo(requestUri.AbsoluteUri);
return await taskCompletionSource.Task;
}
public async Task<AuthenticationResult> AuthenticateForm(Uri requestUri, Uri callbackUri)
{
PopupForm popupForm;
var taskCompletionSource = new TaskCompletionSource<AuthenticationResult>();
popupForm = new PopupForm(callbackUri);
popupForm.FormClosed += (sender, e) =>
{
taskCompletionSource.SetResult(HandleExit(popupForm.ActualUrl));
};
popupForm.Show();
popupForm.NavigateTo(requestUri.AbsoluteUri);
return await taskCompletionSource.Task;
}
private AuthenticationResult HandleExit(Uri actualUrl)
{
var result = new AuthenticationResult();
if (actualUrl != null)
{
var query = System.Web.HttpUtility.ParseQueryString(actualUrl.Query);
result.ResponseData = query.ToString();
result.ResponseStatus = AuthenticationResultStatus.Success;
}
else
{
result.ResponseStatus = AuthenticationResultStatus.ErrorHttp;
}
return result;
}
}
}

Просмотреть файл

@ -1,70 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Runtime.InteropServices;
using System.Text;
using Microsoft.Toolkit.Services.Core;
using static Microsoft.Toolkit.Services.PlatformSpecific.NetFramework.PasswordManagerNativeMethods;
namespace Microsoft.Toolkit.Services.PlatformSpecific.NetFramework
{
internal class NetFrameworkPasswordManager : IPasswordManager
{
public void Store(string resource, PasswordCredential credential)
{
// Validations.
byte[] byteArray = Encoding.Unicode.GetBytes(credential.Password);
// Go ahead with what we have are stuff it into the CredMan structures.
Credential cred = new Credential
{
TargetName = resource,
UserName = credential.UserName,
CredentialBlob = credential.Password,
CredentialBlobSize = (uint)byteArray.Length,
AttributeCount = 0,
Attributes = IntPtr.Zero,
Comment = null,
TargetAlias = null,
Type = CRED_TYPE.GENERIC,
Persist = CRED_PERSIST.LOCAL_MACHINE
};
NativeCredential userCredential = NativeCredential.GetNativeCredential(cred);
// Write the info into the CredMan storage.
bool written = CredWrite(ref userCredential, 0);
int lastError = Marshal.GetLastWin32Error();
if (!written)
{
string message = "CredWrite failed with the error code " + lastError.ToString();
throw new InvalidOperationException(message);
}
}
public PasswordCredential Get(string key)
{
int lastError = Marshal.GetHRForLastWin32Error();
if (!CredRead(key, CRED_TYPE.GENERIC, 0, out var nCredPtr))
{
return null;
}
CriticalCredentialHandle credentialHandle = new CriticalCredentialHandle(nCredPtr);
Credential credential = credentialHandle.GetCredential();
return new PasswordCredential
{
UserName = credential.UserName,
Password = credential.CredentialBlob
};
}
public void Remove(string key)
{
CredDelete(key, CRED_TYPE.GENERIC, 0);
}
}
}

Просмотреть файл

@ -1,40 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Toolkit.Services.Core;
namespace Microsoft.Toolkit.Services.PlatformSpecific.NetFramework
{
internal class NetFrameworkSignatureManager : ISignatureManager
{
/// <summary>
/// Generate request signature.
/// </summary>
/// <param name="baseString">String to sign</param>
/// <param name="secret">Secret to use to sign</param>
/// <param name="append">If true append &amp; to the base string</param>
/// <returns>Signature.</returns>
public string GetSignature(string baseString, string secret, bool append = false)
{
var key = append ? secret + "&" : secret;
var baseStringByte = Encoding.UTF8.GetBytes(baseString);
var keyByte = Encoding.UTF8.GetBytes(key);
using (HMACSHA1 hmac = new HMACSHA1(keyByte))
{
hmac.Initialize();
var hash = hmac.ComputeHash(baseStringByte);
string base64 = Convert.ToBase64String(hash);
return base64;
}
}
}
}

Просмотреть файл

@ -1,57 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.IsolatedStorage;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Toolkit.Services.Core;
namespace Microsoft.Toolkit.Services.PlatformSpecific.NetFramework
{
internal class NetFrameworkStorageManager : IStorageManager
{
private const string FileName = "credential_service_data.txt";
private const char Separator = ':';
public async Task<string> GetAsync(string key)
{
var isoStore = IsolatedStorageFile.GetStore(IsolatedStorageScope.User | IsolatedStorageScope.Assembly, null, null);
using (IsolatedStorageFileStream isoStream = new IsolatedStorageFileStream(FileName, FileMode.OpenOrCreate, isoStore))
{
using (StreamReader reader = new StreamReader(isoStream))
{
while (!reader.EndOfStream)
{
var line = (await reader.ReadLineAsync()).Split(Separator);
var currentKey = line.First();
if (currentKey == key)
{
return line.Last();
}
}
}
}
return null;
}
public Task SetAsync(string key, string value)
{
var isoStore = IsolatedStorageFile.GetStore(IsolatedStorageScope.User | IsolatedStorageScope.Assembly, null, null);
using (IsolatedStorageFileStream isoStream = new IsolatedStorageFileStream(FileName, FileMode.Append, isoStore))
{
using (StreamWriter writer = new StreamWriter(isoStream))
{
return writer.WriteLineAsync(string.Concat(key, Separator, value));
}
}
}
}
}

Просмотреть файл

@ -1,161 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;
namespace Microsoft.Toolkit.Services.PlatformSpecific.NetFramework
{
internal class PasswordManagerNativeMethods
{
[DllImport("Advapi32.dll", EntryPoint = "CredReadW", CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern bool CredRead(string target, CRED_TYPE type, int reservedFlag, out IntPtr credentialPtr);
[DllImport("Advapi32.dll", EntryPoint = "CredWriteW", CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern bool CredWrite([In] ref NativeCredential userCredential, [In] uint flags);
[DllImport("Advapi32.dll", EntryPoint = "CredFree", SetLastError = true)]
internal static extern bool CredFree([In] IntPtr cred);
[DllImport("advapi32.dll", EntryPoint = "CredDeleteW", CharSet = CharSet.Unicode)]
internal static extern bool CredDelete(string target, CRED_TYPE type, int flags);
internal enum CRED_TYPE : uint
{
GENERIC = 1,
DOMAIN_PASSWORD = 2,
DOMAIN_CERTIFICATE = 3,
DOMAIN_VISIBLE_PASSWORD = 4,
GENERIC_CERTIFICATE = 5,
DOMAIN_EXTENDED = 6,
MAXIMUM = 7, // Maximum supported cred type
MAXIMUM_EX = MAXIMUM + 1000, // Allow new applications to run on old OSes
}
internal enum CRED_PERSIST : uint
{
SESSION = 1,
LOCAL_MACHINE = 2,
ENTERPRISE = 3,
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
internal struct NativeCredential
{
internal uint Flags;
internal CRED_TYPE Type;
internal IntPtr TargetName;
internal IntPtr Comment;
internal System.Runtime.InteropServices.ComTypes.FILETIME LastWritten;
internal uint CredentialBlobSize;
internal IntPtr CredentialBlob;
internal uint Persist;
internal uint AttributeCount;
internal IntPtr Attributes;
internal IntPtr TargetAlias;
internal IntPtr UserName;
/// <summary>
/// This method derives a NativeCredential instance from a given Credential instance.
/// </summary>
/// <param name="cred">The managed Credential counterpart containing data to be stored.</param>
/// <returns>A NativeCredential instance that is derived from the given Credential
/// instance.</returns>
internal static NativeCredential GetNativeCredential(Credential cred)
{
return new NativeCredential
{
AttributeCount = 0,
Attributes = IntPtr.Zero,
Comment = IntPtr.Zero,
TargetAlias = IntPtr.Zero,
Type = CRED_TYPE.GENERIC,
Persist = (uint)cred.Persist,
CredentialBlobSize = (uint)cred.CredentialBlobSize,
TargetName = Marshal.StringToCoTaskMemUni(cred.TargetName),
CredentialBlob = Marshal.StringToCoTaskMemUni(cred.CredentialBlob),
UserName = Marshal.StringToCoTaskMemUni(cred.UserName)
};
}
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
internal struct Credential
{
internal uint Flags;
internal CRED_TYPE Type;
internal string TargetName;
internal string Comment;
internal System.Runtime.InteropServices.ComTypes.FILETIME LastWritten;
internal uint CredentialBlobSize;
internal string CredentialBlob;
internal CRED_PERSIST Persist;
internal uint AttributeCount;
internal IntPtr Attributes;
internal string TargetAlias;
internal string UserName;
}
/// <summary>
/// Handle and create the credential.
/// </summary>
internal sealed class CriticalCredentialHandle : CriticalHandleZeroOrMinusOneIsInvalid
{
// Set the handle.
internal CriticalCredentialHandle(IntPtr preexistingHandle)
{
SetHandle(preexistingHandle);
}
internal Credential GetCredential()
{
if (!IsInvalid)
{
// Get the Credential from the mem location
NativeCredential nativeCredential = (NativeCredential)Marshal.PtrToStructure(handle, typeof(NativeCredential));
// Create a managed Credential type and fill it with data from the native counterpart.
return new Credential
{
CredentialBlobSize = nativeCredential.CredentialBlobSize,
CredentialBlob = Marshal.PtrToStringUni(nativeCredential.CredentialBlob, (int)nativeCredential.CredentialBlobSize / 2),
UserName = Marshal.PtrToStringUni(nativeCredential.UserName),
TargetName = Marshal.PtrToStringUni(nativeCredential.TargetName),
TargetAlias = Marshal.PtrToStringUni(nativeCredential.TargetAlias),
Type = nativeCredential.Type,
Flags = nativeCredential.Flags,
Persist = (CRED_PERSIST)nativeCredential.Persist
};
}
else
{
throw new InvalidOperationException("Invalid CriticalHandle!");
}
}
// Perform any specific actions to release the handle in the ReleaseHandle method.
// Often, you need to use PInvoke to make a call into the Win32 API to release the
// handle. In this case, however, we can use the Marshal class to release the unmanaged memory.
protected override bool ReleaseHandle()
{
// If the handle was set, free it. Return success.
if (!IsInvalid)
{
// NOTE: We should also ZERO out the memory allocated to the handle, before freeing it
// so there are no traces of the sensitive data left in memory.
CredFree(handle);
// Mark the handle as invalid for future users.
SetHandleAsInvalid();
return true;
}
// Return false.
return false;
}
}
}
}

Просмотреть файл

@ -1,67 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace Microsoft.Toolkit.Services.PlatformSpecific.NetFramework
{
partial class PopupForm
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.webView1 = new Microsoft.Toolkit.Forms.UI.Controls.WebView();
((System.ComponentModel.ISupportInitialize)(this.webView1)).BeginInit();
this.SuspendLayout();
//
// webView1
//
this.webView1.Dock = System.Windows.Forms.DockStyle.Fill;
this.webView1.Location = new System.Drawing.Point(0, 0);
this.webView1.MinimumSize = new System.Drawing.Size(20, 20);
this.webView1.Name = "webView1";
this.webView1.Size = new System.Drawing.Size(800, 450);
this.webView1.TabIndex = 0;
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(800, 450);
this.Controls.Add(this.webView1);
this.Name = "Form1";
this.Text = "Form1";
((System.ComponentModel.ISupportInitialize)(this.webView1)).EndInit();
this.ResumeLayout(false);
}
#endregion
private Microsoft.Toolkit.Forms.UI.Controls.WebView webView1;
}
}

Просмотреть файл

@ -1,77 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Microsoft.Toolkit.Services.PlatformSpecific.NetFramework
{
/// <summary>
/// Service WebView for windows forms
/// </summary>
public partial class PopupForm : Form
{
private string initialHost;
private string callbackHost;
/// <summary>
/// Gets or sets the current URL before closing the form
/// </summary>
public Uri ActualUrl { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="PopupForm"/> class.
/// </summary>
/// <param name="callbackUrl">Uri callback url</param>
public PopupForm(Uri callbackUrl)
{
InitializeComponent();
webView1.NavigationStarting += (s, e) => WebViewNavigationStartingHandler(e.Uri);
callbackHost = GetTopLevelDomain(callbackUrl);
}
private void WebViewNavigationStartingHandler(Uri uri)
{
var topLevelDomain = GetTopLevelDomain(uri);
if (initialHost != topLevelDomain && topLevelDomain == callbackHost)
{
ActualUrl = uri;
this.Close();
}
}
/// <summary>
/// Loads a given url in the WebView
/// </summary>
/// <param name="url">Url string to navigate to.</param>
public void NavigateTo(string url)
{
initialHost = GetTopLevelDomain(url);
webView1.Navigate(url);
}
private string GetTopLevelDomain(string url)
{
return GetTopLevelDomain(new Uri(url));
}
private string GetTopLevelDomain(Uri url)
{
var hostParts = url.Host.Split('.').Select(x => x.ToString());
if (hostParts.Count() > 1)
{
return hostParts.ElementAt(1);
}
return hostParts.ElementAt(0);
}
}
}

Просмотреть файл

@ -1,13 +0,0 @@
<Window x:Class="Microsoft.Toolkit.Services.PlatformSpecific.NetFramework.PopupWPF"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Microsoft.Toolkit.Services.PlatformSpecific.NetFramework"
xmlns:Controls="clr-namespace:Microsoft.Toolkit.Wpf.UI.Controls;assembly=Microsoft.Toolkit.Wpf.UI.Controls.WebView"
mc:Ignorable="d"
Title="PopupPage" Height="450" Width="800">
<Grid>
<Controls:WebView Grid.Row="0" x:Name="WebView1" Width="800" Height="800" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top"/>
</Grid>
</Window>

Просмотреть файл

@ -1,83 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Forms;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
namespace Microsoft.Toolkit.Services.PlatformSpecific.NetFramework
{
/// <summary>
/// Interaction logic for PopupWPF.xaml
/// </summary>
public partial class PopupWPF : Window
{
private string initialHost;
private string callbackHost;
/// <summary>
/// Gets or sets the current URL before closing the form
/// </summary>
public Uri ActualUrl { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="PopupWPF"/> class.
/// </summary>
/// <param name="callbackUrl">Uri callback url</param>
public PopupWPF(Uri callbackUrl)
{
InitializeComponent();
WebView1.NavigationStarting += (s, e) => WebViewNavigationStartingHandler(e.Uri);
callbackHost = GetTopLevelDomain(callbackUrl);
}
private void WebViewNavigationStartingHandler(Uri uri)
{
var topLevelDomain = GetTopLevelDomain(uri);
if (initialHost != topLevelDomain && topLevelDomain == callbackHost)
{
ActualUrl = uri;
this.Close();
}
}
/// <summary>
/// Loads a given url in the WebView
/// </summary>
/// <param name="url">Url string to navigate to.</param>
public void NavigateTo(string url)
{
initialHost = GetTopLevelDomain(url);
WebView1.Navigate(url);
}
private string GetTopLevelDomain(string url)
{
return GetTopLevelDomain(new Uri(url));
}
private string GetTopLevelDomain(Uri url)
{
var hostParts = url.Host.Split('.').Select(x => x.ToString());
if (hostParts.Count() > 1)
{
return hostParts.ElementAt(1);
}
return hostParts.ElementAt(0);
}
}
}

Просмотреть файл

@ -1,44 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Threading.Tasks;
using Microsoft.Toolkit.Services.Core;
using Windows.Security.Authentication.Web;
namespace Microsoft.Toolkit.Services.PlatformSpecific.Uwp
{
/// <summary>
/// Authentication Broker
/// </summary>
internal class UwpAuthenticationBroker : IAuthenticationBroker
{
/// <summary>
/// Authentication process
/// </summary>
/// <param name="requestUri"> Request Uri</param>
/// <param name="callbackUri"> Uri result</param>
/// <returns> Returns login status</returns>
public async Task<AuthenticationResult> Authenticate(Uri requestUri, Uri callbackUri)
{
WebAuthenticationResult result = await WebAuthenticationBroker.AuthenticateAsync(
WebAuthenticationOptions.None,
requestUri,
callbackUri);
switch (result.ResponseStatus)
{
case WebAuthenticationStatus.Success:
return new AuthenticationResult { ResponseData = result.ResponseData, ResponseStatus = AuthenticationResultStatus.Success };
case WebAuthenticationStatus.UserCancel:
return new AuthenticationResult { ResponseData = result.ResponseData, ResponseStatus = AuthenticationResultStatus.UserCancel, ResponseErrorDetail = result.ResponseErrorDetail };
case WebAuthenticationStatus.ErrorHttp:
return new AuthenticationResult { ResponseData = result.ResponseData, ResponseStatus = AuthenticationResultStatus.ErrorHttp, ResponseErrorDetail = result.ResponseErrorDetail };
default:
// TODO: Change with correct name;
throw new ArgumentException("error");
}
}
}
}

Просмотреть файл

@ -1,67 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Linq;
using Microsoft.Toolkit.Services.Core;
using Windows.Security.Credentials;
namespace Microsoft.Toolkit.Services.PlatformSpecific.Uwp
{
/// <summary>
/// Password Manager
/// </summary>
internal class UwpPasswordManager : IPasswordManager
{
/// <summary>
/// Password vault used to store access tokens
/// </summary>
private readonly PasswordVault _vault;
/// <summary>
/// Initializes a new instance of the <see cref="UwpPasswordManager"/> class.
/// </summary>
public UwpPasswordManager()
{
_vault = new PasswordVault();
}
/// <inheritdoc/>
public Toolkit.Services.Core.PasswordCredential Get(string key)
{
var credentials = RetrievePasswordCredential(key);
if (credentials == null)
{
return null;
}
return new Toolkit.Services.Core.PasswordCredential { Password = credentials.Password, UserName = credentials.UserName };
}
private Windows.Security.Credentials.PasswordCredential RetrievePasswordCredential(string key)
{
var passwordCredentials = _vault.RetrieveAll();
var temp = passwordCredentials.FirstOrDefault(c => c.Resource == key);
if (temp == null)
{
return null;
}
return _vault.Retrieve(temp.Resource, temp.UserName);
}
/// <inheritdoc/>
public void Remove(string key)
{
_vault.Remove(RetrievePasswordCredential(key));
}
/// <inheritdoc/>
public void Store(string resource, Toolkit.Services.Core.PasswordCredential credentials)
{
var passwordCredential = new Windows.Security.Credentials.PasswordCredential(resource, credentials.UserName, credentials.Password);
_vault.Add(passwordCredential);
}
}
}

Просмотреть файл

@ -1,37 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using Microsoft.Toolkit.Services.Core;
using Windows.Security.Cryptography;
using Windows.Security.Cryptography.Core;
using Windows.Storage.Streams;
namespace Microsoft.Toolkit.Services.PlatformSpecific.Uwp
{
/// <summary>
/// UWP specific signature generator using cryptographic library
/// </summary>
internal class UwpSignatureManager : ISignatureManager
{
/// <summary>
/// Generate request signature.
/// </summary>
/// <param name="baseString">String to sign</param>
/// <param name="secret">Secret to use to sign</param>
/// <param name="append">If true append &amp; to the base string</param>
/// <returns>Signature.</returns>
public string GetSignature(string baseString, string secret, bool append = false)
{
var key = append ? secret + "&" : secret;
IBuffer keyMaterial = CryptographicBuffer.ConvertStringToBinary(key, BinaryStringEncoding.Utf8);
MacAlgorithmProvider mac = MacAlgorithmProvider.OpenAlgorithm(MacAlgorithmNames.HmacSha1);
CryptographicKey cryptoKey = mac.CreateKey(keyMaterial);
IBuffer dataToBeSigned = CryptographicBuffer.ConvertStringToBinary(baseString, BinaryStringEncoding.Utf8);
IBuffer hash = CryptographicEngine.Sign(cryptoKey, dataToBeSigned);
return CryptographicBuffer.EncodeToBase64String(hash);
}
}
}

Просмотреть файл

@ -1,40 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Toolkit.Services.Core;
using Windows.Security.Credentials;
using Windows.Storage;
namespace Microsoft.Toolkit.Services.PlatformSpecific.Uwp
{
/// <summary>
/// UWP specific implementation for IStorageManager using ApplicationData and LocalSettings
/// </summary>
internal class UwpStorageManager : IStorageManager
{
/// <summary>
/// Read the storage to return the key if exists if not null;
/// </summary>
/// <param name="key">Key to lookup</param>
/// <returns>Return string value if exists if not null</returns>
public Task<string> GetAsync(string key)
{
return Task.FromResult<string>(ApplicationData.Current.LocalSettings.Values[key]?.ToString());
}
/// <summary>
/// Save the value in the key inside the storage
/// </summary>
/// <param name="key">Key name in storage</param>
/// <param name="value">Value associated to the storage</param>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public Task SetAsync(string key, string value)
{
ApplicationData.Current.LocalSettings.Values[key] = value;
return Task.CompletedTask;
}
}
}

Просмотреть файл

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Directives xmlns="http://schemas.microsoft.com/netfx/2013/01/metadata">
<Library Name="Microsoft.Toolkit.Services">
<Namespace Name="System.Text.Json.Serialization.Converters" Dynamic="Required All"/>
</Library>
</Directives>

Просмотреть файл

@ -1,22 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace Microsoft.Toolkit.Services.LinkedIn
{
/// <summary>
/// Constant strings for LinkedIn Service.
/// </summary>
public static class LinkedInConstants
{
/// <summary>
/// Storage key name for access token.
/// </summary>
public static readonly string STORAGEKEYACCESSTOKEN = "LinkedInAccessToken";
/// <summary>
/// Storage key name for user name.
/// </summary>
public static readonly string STORAGEKEYUSER = "LinkedInUser";
}
}

Просмотреть файл

@ -1,32 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace Microsoft.Toolkit.Services.LinkedIn
{
/// <summary>
/// Strong type representation of Content.
/// </summary>
public class LinkedInContent
{
/// <summary>
/// Gets or sets title property.
/// </summary>
public string Title { get; set; }
/// <summary>
/// Gets or sets description property.
/// </summary>
public string Description { get; set; }
/// <summary>
/// Gets or sets submitted url property.
/// </summary>
public string SubmittedUrl { get; set; }
/// <summary>
/// Gets or sets submitted image url property.
/// </summary>
public string SubmittedImageUrl { get; set; }
}
}

Просмотреть файл

@ -1,17 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace Microsoft.Toolkit.Services.LinkedIn
{
/// <summary>
/// Configuration object for specifying richer query information.
/// </summary>
public class LinkedInDataConfig
{
/// <summary>
/// Gets or sets the query string for filtering service results.
/// </summary>
public string Query { get; set; }
}
}

Просмотреть файл

@ -1,334 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.Toolkit.Services.Core;
#if WINRT
using Microsoft.Toolkit.Services.PlatformSpecific.Uwp;
#endif
#if NET462
using Microsoft.Toolkit.Services.PlatformSpecific.NetFramework;
#endif
namespace Microsoft.Toolkit.Services.LinkedIn
{
/// <summary>
/// Data Provider for connecting to LinkedIn service.
/// </summary>
public class LinkedInDataProvider
{
private const string _oAuthBaseUrl = "https://www.linkedin.com/uas/oauth2/";
private const string _baseUrl = "https://api.linkedin.com/v1";
private static HttpClient client = new HttpClient();
/// <summary>
/// Gets or sets logged in user information.
/// </summary>
public string Username { get; set; }
/// <summary>
/// Gets a value indicating whether the provider is already logged in
/// </summary>
public bool LoggedIn { get; private set; }
/// <summary>
/// Gets or sets requiredPermissions property.
/// </summary>
public LinkedInPermissions RequiredPermissions { get; set; }
private readonly IAuthenticationBroker _authentication;
private readonly IPasswordManager _passwordManager;
private readonly IStorageManager _storageManager;
/// <summary>
/// Gets or sets tokens property.
/// </summary>
public LinkedInOAuthTokens Tokens { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="LinkedInDataProvider"/> class.
/// Constructor.
/// </summary>
/// <param name="tokens">OAuth tokens for request.</param>
/// <param name="requiredPermissions">Required permissions for the session.</param>
/// <param name="authentication">Authentication result interface.</param>
/// <param name="passwordManager">Password Manager interface, store the password.</param>
/// <param name="storageManager">Storage Manager interface.</param>
public LinkedInDataProvider(LinkedInOAuthTokens tokens, LinkedInPermissions requiredPermissions, IAuthenticationBroker authentication, IPasswordManager passwordManager, IStorageManager storageManager)
{
if (string.IsNullOrEmpty(tokens.ClientSecret))
{
throw new ArgumentException("Missing client secret key");
}
if (string.IsNullOrEmpty(tokens.ClientId))
{
throw new ArgumentException("Missing client ID");
}
if (string.IsNullOrEmpty(tokens.CallbackUri))
{
throw new ArgumentException("Missing callback uri");
}
// Check if its a valid combination of LinkedInPermissions
if ((~(int)LinkedInPermissionsHelpers.AllPermissions & (int)requiredPermissions) != 0)
{
throw new ArgumentException("Error retrieving required permissions");
}
Tokens = tokens;
RequiredPermissions = requiredPermissions;
_authentication = authentication ?? throw new ArgumentException("Invalid AuthenticationBroker");
_storageManager = storageManager ?? throw new ArgumentException("Invalid StorageManager");
_passwordManager = passwordManager ?? throw new ArgumentException("Invalid PasswordManager");
}
#if WINRT
/// <summary>
/// Initializes a new instance of the <see cref="LinkedInDataProvider"/> class.
/// Constructor.
/// </summary>
/// <param name="tokens">OAuth tokens for request.</param>
/// <param name="requiredPermissions">Required permissions for the session.</param>
public LinkedInDataProvider(LinkedInOAuthTokens tokens, LinkedInPermissions requiredPermissions)
: this(tokens, requiredPermissions, new UwpAuthenticationBroker(), new UwpPasswordManager(), new UwpStorageManager())
{
}
#endif
#if NET462
/// <summary>
/// Initializes a new instance of the <see cref="LinkedInDataProvider"/> class.
/// Constructor.
/// </summary>
/// <param name="tokens">OAuth tokens for request.</param>
/// <param name="requiredPermissions">Required permissions for the session.</param>
public LinkedInDataProvider(LinkedInOAuthTokens tokens, LinkedInPermissions requiredPermissions)
: this(tokens, requiredPermissions, new NetFrameworkAuthenticationBroker(), new NetFrameworkPasswordManager(), new NetFrameworkStorageManager())
{
}
#endif
/// <summary>
/// Log user in to LinkedIn.
/// </summary>
/// <returns>Boolean indicating login success.</returns>
public async Task<bool> LoginAsync()
{
var user = await _storageManager.GetAsync(LinkedInConstants.STORAGEKEYUSER);
var credential = _passwordManager.Get(LinkedInConstants.STORAGEKEYACCESSTOKEN);
if (!string.IsNullOrEmpty(user) && credential != null)
{
Tokens.AccessToken = credential.Password;
Username = user;
LoggedIn = true;
return true;
}
string authorizeCode = await GetAuthorizeCodeAsync(Tokens, RequiredPermissions);
if (!string.IsNullOrEmpty(authorizeCode))
{
var accessToken = await GetAccessTokenAsync(Tokens, authorizeCode);
if (!string.IsNullOrEmpty(accessToken))
{
Tokens.AccessToken = accessToken;
_passwordManager.Store(LinkedInConstants.STORAGEKEYACCESSTOKEN, new PasswordCredential { UserName = LinkedInConstants.STORAGEKEYUSER, Password = accessToken });
await _storageManager.SetAsync(LinkedInConstants.STORAGEKEYUSER, LinkedInConstants.STORAGEKEYUSER);
return true;
}
}
LoggedIn = false;
return false;
}
/// <summary>
/// Log user out of LinkedIn.
/// </summary>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public async Task LogoutAsync()
{
var credential = _passwordManager.Get(LinkedInConstants.STORAGEKEYACCESSTOKEN);
if (credential != null)
{
_passwordManager.Remove(LinkedInConstants.STORAGEKEYACCESSTOKEN);
await _storageManager.SetAsync(LinkedInConstants.STORAGEKEYUSER, null);
}
LoggedIn = false;
}
/// <summary>
/// Wrapper around REST API for making data request.
/// </summary>
/// <typeparam name="TSchema">Schema to use</typeparam>
/// <param name="config">Query configuration.</param>
/// <param name="maxRecords">Upper limit for records returned.</param>
/// <param name="startRecord">Index of paged results.</param>
/// <param name="fields">A comma separated string of required fields, which will have strongly typed representation in the model passed in.</param>
/// <returns>Strongly typed list of results.</returns>
public async Task<IEnumerable<TSchema>> GetDataAsync<TSchema>(LinkedInDataConfig config, int maxRecords, int startRecord = 0, string fields = "id")
{
var parser = new LinkedInParser<TSchema>();
var url = $"{_baseUrl}{config.Query}/~:({fields})?oauth2_access_token={Tokens.AccessToken}&format=json&count={maxRecords}&start={startRecord}";
using HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, new Uri(url));
request.Headers.Connection.TryParseAdd("Keep-Alive");
using var response = await client.SendAsync(request).ConfigureAwait(false);
var data = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
if (response.IsSuccessStatusCode && !string.IsNullOrEmpty(data))
{
return parser.Parse(data);
}
throw new RequestFailedException((System.Net.HttpStatusCode)response.StatusCode, data);
}
/// <summary>
/// Share data to LinkedIn.
/// </summary>
/// <typeparam name="T">Schema of data to share.</typeparam>
/// <typeparam name="U">Type of response object.</typeparam>
/// <param name="dataToShare">Share request content.</param>
/// <returns>Boolean indicating success or failure.</returns>
public async Task<U> ShareDataAsync<T, U>(T dataToShare)
{
var shareRequest = dataToShare as LinkedInShareRequest;
if (shareRequest != null)
{
LinkedInVisibility.ParseVisibilityStringToEnum(shareRequest.Visibility.Code);
var requestParser = new LinkedInParser<LinkedInShareRequest>();
var url = $"{_baseUrl}/people/~/shares?oauth2_access_token={Tokens.AccessToken}&format=json";
using HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, new Uri(url));
request.Headers.Add("x-li-format", "json");
var stringContent = requestParser.Parse(shareRequest);
request.Content = new StringContent(stringContent, Encoding.UTF8, "application/json");
using var response = await client.SendAsync(request).ConfigureAwait(false);
var data = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
var responseParser = new LinkedInParser<U>();
var listResults = responseParser.Parse(data) as List<U>;
return listResults[0];
}
return default(U);
}
/// <summary>
/// Check validity of configuration.
/// </summary>
/// <param name="config">Query configuration.</param>
protected void ValidateConfig(LinkedInDataConfig config)
{
if (config?.Query == null)
{
throw new ConfigParameterNullException(nameof(config.Query));
}
}
private async Task<string> GetAccessTokenAsync(LinkedInOAuthTokens tokens, string authorizeCode)
{
var url = $"{_oAuthBaseUrl}accessToken?grant_type=authorization_code"
+ "&code=" + authorizeCode
+ "&redirect_uri=" + Uri.EscapeDataString(tokens.CallbackUri)
+ "&client_id=" + tokens.ClientId
+ "&client_secret=" + tokens.ClientSecret;
using var request = new HttpRequestMessage(HttpMethod.Post, new Uri(url));
using var response = await client.SendAsync(request).ConfigureAwait(false);
using var jsonStream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
using var jsonDoc = await JsonDocument.ParseAsync(jsonStream).ConfigureAwait(false);
var value = jsonDoc.RootElement.GetProperty("access_token");
return value.GetString();
}
private async Task<string> GetAuthorizeCodeAsync(LinkedInOAuthTokens tokens, LinkedInPermissions permissions)
{
string scopes = ConvertPermissionsToEncodedScopeString(permissions);
var url = $"{_oAuthBaseUrl}authorization?response_type=code"
+ "&client_id=" + tokens.ClientId
+ "&state=STATE"
+ "&redirect_uri=" + Uri.EscapeDataString(tokens.CallbackUri)
+ "&" + scopes;
var startUri = new Uri(url);
var endUri = new Uri(tokens.CallbackUri);
var result = await _authentication.Authenticate(startUri, endUri);
switch (result.ResponseStatus)
{
case AuthenticationResultStatus.Success:
{
var response = result.ResponseData;
IDictionary<string, string> dictionary = new Dictionary<string, string>();
var split = response.Split('?');
foreach (var keyValue in split[split.Length - 1].Split('&'))
{
var keyValueSplit = keyValue.Split('=');
if (keyValueSplit.Length == 2)
{
dictionary.Add(keyValueSplit[0], keyValueSplit[1]);
}
}
return dictionary["code"];
}
case AuthenticationResultStatus.ErrorHttp:
Debug.WriteLine("WAB failed, message={0}", result.ResponseErrorDetail.ToString());
return string.Empty;
case AuthenticationResultStatus.UserCancel:
Debug.WriteLine("WAB user aborted.");
return string.Empty;
}
return string.Empty;
}
private string ConvertPermissionsToEncodedScopeString(LinkedInPermissions requiredPermissions)
{
StringBuilder scope = new StringBuilder();
foreach (LinkedInPermissions value in Enum.GetValues(typeof(LinkedInPermissions)))
{
if ((requiredPermissions & value) != LinkedInPermissions.NotSet)
{
var name = value.ToString().ToLower();
name = name.Replace("readwrite", "rw_");
name = name.Replace("read", "r_");
name = name.Replace("write", "w_");
name = name.Replace("companyadmin", "company_admin");
scope.Append($"{name} ");
}
}
return "scope=" + Uri.EscapeDataString(scope.ToString());
}
}
}

Просмотреть файл

@ -1,37 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace Microsoft.Toolkit.Services.LinkedIn
{
/// <summary>
/// LinkedIn OAuth tokens.
/// </summary>
public class LinkedInOAuthTokens
{
/// <summary>
/// Gets or sets clientId.
/// </summary>
public string ClientId { get; set; }
/// <summary>
/// Gets or sets clientSecret.
/// </summary>
public string ClientSecret { get; set; }
/// <summary>
/// Gets or sets callback Uri.
/// </summary>
public string CallbackUri { get; set; }
/// <summary>
/// Gets or sets access token.
/// </summary>
public string AccessToken { get; set; }
/// <summary>
/// Gets or sets access token Secret.
/// </summary>
public string AccessTokenSecret { get; set; }
}
}

Просмотреть файл

@ -1,48 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Collections.Generic;
using System.Text.Json;
namespace Microsoft.Toolkit.Services.LinkedIn
{
/// <summary>
/// Parse results into strong type.
/// </summary>
/// <typeparam name="T">Type to parse into.</typeparam>
public class LinkedInParser<T>
{
/// <summary>
/// Take string data and parse into strong data type.
/// </summary>
/// <param name="data">String data.</param>
/// <returns>Returns strong type.</returns>
public IEnumerable<T> Parse(string data)
{
List<T> results;
try
{
results = JsonSerializer.Deserialize<List<T>>(data);
}
catch (JsonException)
{
T linkedInResult = JsonSerializer.Deserialize<T>(data);
results = new List<T> { linkedInResult };
}
return results;
}
/// <summary>
/// Take strong type and return corresponding JSON string.
/// </summary>
/// <param name="dataToShare">Strong typed instance.</param>
/// <returns>Returns string data.</returns>
public string Parse(T dataToShare)
{
return JsonSerializer.Serialize(dataToShare, typeof(T), new JsonSerializerOptions { IgnoreNullValues = true });
}
}
}

Просмотреть файл

@ -1,54 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
namespace Microsoft.Toolkit.Services.LinkedIn
{
/// <summary>
/// List of user related data permissions
/// </summary>
[Flags]
public enum LinkedInPermissions
{
/// <summary>
/// Not set
/// </summary>
NotSet = 0,
/// <summary>
/// Read - Basic profile (r_basicprofile)
/// </summary>
ReadBasicProfile = 1,
/// <summary>
/// Read - Email Address (r_emailaddress)
/// </summary>
ReadEmailAddress = 2,
/// <summary>
/// Read / Write - Company Admin (rw_company_admin)
/// </summary>
ReadWriteCompanyAdmin = 4,
/// <summary>
/// Write - Share (w_share)
/// </summary>
WriteShare = 8
}
#pragma warning disable SA1649 // File name should match first type name
internal static class LinkedInPermissionsHelpers
{
/// <summary>
/// Internal AllPermissions for LinkedInPermissions, so we don't expose it. Keep it in sync with <see cref="LinkedInPermissions"/>
/// </summary>
internal const LinkedInPermissions AllPermissions =
LinkedInPermissions.ReadBasicProfile |
LinkedInPermissions.ReadEmailAddress |
LinkedInPermissions.ReadWriteCompanyAdmin |
LinkedInPermissions.WriteShare;
}
#pragma warning restore SA1649 // File name should match first type name
}

Просмотреть файл

@ -1,67 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace Microsoft.Toolkit.Services.LinkedIn
{
/// <summary>
/// Strongly typed LinkedIn Basic Profile. More details here https://developer.linkedin.com/docs/fields/basic-profile.
/// </summary>
public partial class LinkedInProfile
{
/// <summary>
/// Gets a string description of the strongly typed properties in this model.
/// </summary>
public static string Fields => "first-name,last-name,headline,id,picture-url,site-standard-profile-request,num-connections,summary,public-profile-url";
/// <summary>
/// Gets or sets firstName property.
/// </summary>
public string FirstName { get; set; }
/// <summary>
/// Gets or sets headline property.
/// </summary>
public string Headline { get; set; }
/// <summary>
/// Gets or sets id property.
/// </summary>
public string Id { get; set; }
/// <summary>
/// Gets or sets lastname property.
/// </summary>
public string LastName { get; set; }
/// <summary>
/// Gets or sets picture-url property.
/// </summary>
public string PictureUrl { get; set; }
/// <summary>
/// Gets or sets num-connections property.
/// </summary>
public string NumConnections { get; set; }
/// <summary>
/// Gets or sets summary property.
/// </summary>
public string Summary { get; set; }
/// <summary>
/// Gets or sets public-profile-url property.
/// </summary>
public string PublicProfileUrl { get; set; }
/// <summary>
/// Gets or sets email-address property. Requires r_emailaddress permission.
/// </summary>
public string EmailAddress { get; set; }
/// <summary>
/// Gets or sets siteStandardProfileRequest property.
/// </summary>
public LinkedInProfileRequest SiteStandardProfileRequest { get; set; }
}
}

Просмотреть файл

@ -1,17 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace Microsoft.Toolkit.Services.LinkedIn
{
/// <summary>
/// Strongly typed SiteStandardProfileRequest class.
/// </summary>
public class LinkedInProfileRequest
{
/// <summary>
/// Gets or sets url property.
/// </summary>
public string Url { get; set; }
}
}

Просмотреть файл

@ -1,276 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.Toolkit.Services.Core;
#if WINRT
using Microsoft.Toolkit.Services.PlatformSpecific.Uwp;
#endif
#if NET462
using Microsoft.Toolkit.Services.PlatformSpecific.NetFramework;
#endif
namespace Microsoft.Toolkit.Services.LinkedIn
{
/// <summary>
/// Class for connecting to LinkedIn.
/// </summary>
public class LinkedInService
{
/// <summary>
/// Private singleton field.
/// </summary>
private static LinkedInService _instance;
/// <summary>
/// Gets public singleton property.
/// </summary>
public static LinkedInService Instance => _instance ?? (_instance = new LinkedInService());
private LinkedInDataProvider _provider;
private LinkedInOAuthTokens _oAuthTokens;
private LinkedInPermissions _requiredPermissions;
private IAuthenticationBroker _authenticationBroker;
private IPasswordManager _passwordManager;
private IStorageManager _storageManager;
private bool _isInitialized = false;
/// <summary>
/// Gets a reference to an instance of the underlying data provider.
/// </summary>
public LinkedInDataProvider Provider => _provider ?? (_provider = new LinkedInDataProvider(_oAuthTokens, _requiredPermissions, _authenticationBroker, _passwordManager, _storageManager));
private LinkedInService()
{
}
/// <summary>
/// Log user in to LinkedIn.
/// </summary>
/// <returns>Returns success or failure of login attempt.</returns>
public Task<bool> LoginAsync()
{
if (!_isInitialized)
{
throw new InvalidOperationException("Initialized needs to be called first.");
}
return Provider.LoginAsync();
}
/// <summary>
/// Share content to LinkedIn.
/// </summary>
/// <param name="commentContainingUrl">Comment containing a Url.</param>
/// <param name="visibilityCode">Code for who to share with.</param>
/// <returns>Boolean indicating success or failure.</returns>
public Task<LinkedInShareResponse> ShareActivityAsync(string commentContainingUrl, LinkedInShareVisibility visibilityCode = LinkedInShareVisibility.ConnectionsOnly)
{
var shareRequest = new LinkedInShareRequest
{
Comment = commentContainingUrl,
Visibility = new LinkedInVisibility { Code = LinkedInVisibility.ParseVisibilityEnumToString(visibilityCode) }
};
return ShareActivityAsync(shareRequest);
}
/// <summary>
/// Share content to LinkedIn.
/// </summary>
/// <param name="shareRequest">Share request.</param>
/// <returns>Boolean indicating success or failure.</returns>
public Task<LinkedInShareResponse> ShareActivityAsync(LinkedInShareRequest shareRequest)
{
return Provider.ShareDataAsync<LinkedInShareRequest, LinkedInShareResponse>(shareRequest);
}
/// <summary>
/// Log user out of LinkedIn.
/// </summary>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public Task LogoutAsync()
{
_isInitialized = false;
return Provider.LogoutAsync();
}
#if WINRT
/// <summary>
/// Initialize underlying provider with relevant token information for UWP.
/// </summary>
/// <param name="oAuthTokens">Token instance.</param>
/// <param name="requiredPermissions">Scope / permissions app requires user to sign up for.</param>
/// <returns>Success or failure.</returns>
public bool Initialize(LinkedInOAuthTokens oAuthTokens, LinkedInPermissions requiredPermissions = LinkedInPermissions.NotSet)
{
return Initialize(oAuthTokens, new UwpAuthenticationBroker(), new UwpPasswordManager(), new UwpStorageManager(), requiredPermissions);
}
/// <summary>
/// Initialize underlying provider with relevant token information.
/// </summary>
/// <param name="clientId">Client Id.</param>
/// <param name="clientSecret">Client secret.</param>
/// <param name="callbackUri">Callback URI. Has to match callback URI defined at www.linkedin.com/developer/apps/ (can be arbitrary).</param>
/// <returns>Success or failure.</returns>
public bool Initialize(string clientId, string clientSecret, string callbackUri)
{
return Initialize(clientId, clientSecret, callbackUri, new UwpAuthenticationBroker(), new UwpPasswordManager(), new UwpStorageManager());
}
#endif
#if NET462
/// <summary>
/// Initialize underlying provider with relevant token information for UWP.
/// </summary>
/// <param name="oAuthTokens">Token instance.</param>
/// <param name="requiredPermissions">Scope / permissions app requires user to sign up for.</param>
/// <returns>Success or failure.</returns>
public bool Initialize(LinkedInOAuthTokens oAuthTokens, LinkedInPermissions requiredPermissions = LinkedInPermissions.NotSet)
{
return Initialize(oAuthTokens, new NetFrameworkAuthenticationBroker(), new NetFrameworkPasswordManager(), new NetFrameworkStorageManager(), requiredPermissions);
}
/// <summary>
/// Initialize underlying provider with relevant token information.
/// </summary>
/// <param name="clientId">Client Id.</param>
/// <param name="clientSecret">Client secret.</param>
/// <param name="callbackUri">Callback URI. Has to match callback URI defined at www.linkedin.com/developer/apps/ (can be arbitrary).</param>
/// <returns>Success or failure.</returns>
public bool Initialize(string clientId, string clientSecret, string callbackUri)
{
return Initialize(clientId, clientSecret, callbackUri, new NetFrameworkAuthenticationBroker(), new NetFrameworkPasswordManager(), new NetFrameworkStorageManager());
}
#endif
/// <summary>
/// Initialize underlying provider with relevant token information.
/// </summary>
/// <param name="clientId">Client Id.</param>
/// <param name="clientSecret">Client secret.</param>
/// <param name="callbackUri">Callback URI. Has to match callback URI defined at www.linkedin.com/developer/apps/ (can be arbitrary).</param>
/// <param name="authentication">Authentication result interface.</param>
/// <param name="passwordManager">Password Manager interface, store the password.</param>
/// <param name="storageManager">Storage Manager interface.</param>
/// <returns>Success or failure.</returns>
public bool Initialize(string clientId, string clientSecret, string callbackUri, IAuthenticationBroker authentication, IPasswordManager passwordManager, IStorageManager storageManager)
{
if (string.IsNullOrEmpty(clientId))
{
throw new ArgumentNullException(nameof(clientId));
}
if (string.IsNullOrEmpty(clientSecret))
{
throw new ArgumentNullException(nameof(clientSecret));
}
if (string.IsNullOrEmpty(callbackUri))
{
throw new ArgumentNullException(nameof(callbackUri));
}
var oAuthTokens = new LinkedInOAuthTokens
{
ClientId = clientId,
ClientSecret = clientSecret,
CallbackUri = callbackUri
};
return Initialize(oAuthTokens, authentication, passwordManager, storageManager, LinkedInPermissions.ReadBasicProfile);
}
/// <summary>
/// Initialize underlying provider with relevant token information.
/// </summary>
/// <param name="oAuthTokens">Token instance.</param>
/// <param name="authentication">Authentication result interface.</param>
/// <param name="passwordManager">Password Manager interface, store the password.</param>
/// <param name="storageManager">Storage Manager interface.</param>
/// <param name="requiredPermissions">Scope / permissions app requires user to sign up for.</param>
/// <returns>Success or failure.</returns>
public bool Initialize(LinkedInOAuthTokens oAuthTokens, IAuthenticationBroker authentication, IPasswordManager passwordManager, IStorageManager storageManager, LinkedInPermissions requiredPermissions = LinkedInPermissions.NotSet)
{
_oAuthTokens = oAuthTokens ?? throw new ArgumentNullException(nameof(oAuthTokens));
_authenticationBroker = authentication ?? throw new ArgumentNullException(nameof(authentication));
_storageManager = storageManager ?? throw new ArgumentNullException(nameof(storageManager));
_passwordManager = passwordManager ?? throw new ArgumentNullException(nameof(passwordManager));
_requiredPermissions = requiredPermissions;
Provider.RequiredPermissions = requiredPermissions;
Provider.Tokens = oAuthTokens;
_isInitialized = true;
return true;
}
/// <summary>
/// Request list data from service provider based upon a given config / query.
/// </summary>
/// <typeparam name="T">Strong type of model.</typeparam>
/// <param name="config">LinkedInDataConfig instance.</param>
/// <param name="maxRecords">Upper limit of records to return.</param>
/// <param name="startRecord">Index of paged results.</param>
/// <param name="fields">A comma separated string of required fields, which will have strongly typed representation in the model passed in.</param>
/// <returns>Strongly typed list of data returned from the service.</returns>
public async Task<List<T>> RequestAsync<T>(LinkedInDataConfig config, int maxRecords = 20, int startRecord = 0, string fields = "id")
{
List<T> queryResults = new List<T>();
var results = await Provider.GetDataAsync<T>(config, maxRecords, startRecord, fields);
foreach (var result in results)
{
queryResults.Add(result);
}
return queryResults;
}
/// <summary>
/// Retrieve logged in users profile details.
/// </summary>
/// <param name="requireEmailAddress">Require email address - which needs user consensus.</param>
/// <returns>Strongly typed profile.</returns>
public async Task<LinkedInProfile> GetUserProfileAsync(bool requireEmailAddress = false)
{
var fields = LinkedInProfile.Fields;
if (requireEmailAddress)
{
if (!_requiredPermissions.HasFlag(LinkedInPermissions.ReadEmailAddress))
{
throw new InvalidOperationException("Please re-initialize with email permission and call LoginAsync again so user may grant access.");
}
fields += ",email-address";
}
if (Provider.LoggedIn)
{
var results = await LinkedInService.Instance.RequestAsync<LinkedInProfile>(new LinkedInDataConfig { Query = "/people" }, 1, 0, fields);
return results[0];
}
var isLoggedIn = await LoginAsync();
if (isLoggedIn)
{
return await GetUserProfileAsync();
}
return null;
}
}
}

Просмотреть файл

@ -1,27 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace Microsoft.Toolkit.Services.LinkedIn
{
/// <summary>
/// Strong type for sharing data to LinkedIn.
/// </summary>
public partial class LinkedInShareRequest
{
/// <summary>
/// Gets or sets comment property.
/// </summary>
public string Comment { get; set; }
/// <summary>
/// Gets or sets visibility property.
/// </summary>
public LinkedInVisibility Visibility { get; set; }
/// <summary>
/// Gets or sets content property.
/// </summary>
public LinkedInContent Content { get; set; }
}
}

Просмотреть файл

@ -1,22 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace Microsoft.Toolkit.Services.LinkedIn
{
/// <summary>
/// Strong type for Share Response.
/// </summary>
public class LinkedInShareResponse
{
/// <summary>
/// Gets or sets UpdateKey property.
/// </summary>
public string UpdateKey { get; set; }
/// <summary>
/// Gets or sets UpdateUrl property.
/// </summary>
public string UpdateUrl { get; set; }
}
}

Просмотреть файл

@ -1,25 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
namespace Microsoft.Toolkit.Services.LinkedIn
{
/// <summary>
/// List of user related data permissions
/// </summary>
[Flags]
public enum LinkedInShareVisibility
{
/// <summary>
/// Connections only
/// </summary>
ConnectionsOnly = 1,
/// <summary>
/// Anyone
/// </summary>
Anyone = 2
}
}

Просмотреть файл

@ -1,58 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
namespace Microsoft.Toolkit.Services.LinkedIn
{
/// <summary>
/// Strong type representation of Visibility.
/// </summary>
public class LinkedInVisibility
{
private const string ANYONE = "anyone";
private const string CONNECTIONSONLY = "connections-only";
/// <summary>
/// Gets or sets code property.
/// </summary>
public string Code { get; set; }
/// <summary>
/// Converts enum counterpart to appropriate data string.
/// </summary>
/// <param name="visibility">Enumeration.</param>
/// <returns>String representation</returns>
public static string ParseVisibilityEnumToString(LinkedInShareVisibility visibility)
{
switch (visibility)
{
case LinkedInShareVisibility.Anyone:
return ANYONE;
case LinkedInShareVisibility.ConnectionsOnly:
return CONNECTIONSONLY;
}
return string.Empty;
}
/// <summary>
/// Converts string to enum counterpart.
/// </summary>
/// <param name="visibility">String.</param>
/// <returns>Enumeration.</returns>
public static LinkedInShareVisibility ParseVisibilityStringToEnum(string visibility)
{
switch (visibility.ToLower())
{
case ANYONE:
return LinkedInShareVisibility.Anyone;
case CONNECTIONSONLY:
return LinkedInShareVisibility.ConnectionsOnly;
}
throw new ArgumentException("Invalid visibility string supplied.");
}
}
}

Просмотреть файл

@ -1,114 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Net.Http;
using System.Text.Json;
using System.Threading.Tasks;
namespace Microsoft.Toolkit.Services.MicrosoftTranslator
{
/// <summary>
/// Client to call Cognitive Services Azure Auth Token service in order to get an access token.
/// </summary>
internal class AzureAuthToken
{
/// <summary>
/// Name of header used to pass the subscription key to the token service
/// </summary>
private const string OcpApimSubscriptionKeyHeader = "Ocp-Apim-Subscription-Key";
/// <summary>
/// URL of the token service
/// </summary>
private static readonly Uri ServiceUrl = new Uri("https://api.cognitive.microsoft.com/sts/v1.0/issueToken");
// TODO
// private static readonly Uri ServiceUrl = new Uri(THIS SHOULD BE A PARAMETER NOW);
/// <summary>
/// After obtaining a valid token, this class will cache it for this duration.
/// Use a duration of 8 minutes, which is less than the actual token lifetime of 10 minutes.
/// </summary>
private static readonly TimeSpan TokenCacheDuration = new TimeSpan(0, 8, 0);
private static HttpClient client = new HttpClient();
private string _storedTokenValue = string.Empty;
private DateTime _storedTokenTime = DateTime.MinValue;
private string _subscriptionKey;
/// <summary>
/// Gets or sets the Service Subscription Key.
/// </summary>
public string SubscriptionKey
{
get
{
return _subscriptionKey;
}
set
{
if (_subscriptionKey != value)
{
// If the subscription key is changed, the token is no longer valid.
_subscriptionKey = value;
_storedTokenValue = string.Empty;
}
}
}
/// <summary>
/// Initializes a new instance of the <see cref="AzureAuthToken"/> class, that is used to obtain access token
/// </summary>
/// <param name="key">Subscription key to use to get an authentication token.</param>
public AzureAuthToken(string key)
{
SubscriptionKey = key;
}
/// <summary>
/// Gets a token for the specified subscription.
/// </summary>
/// <returns>The encoded JWT token prefixed with the string "Bearer ".</returns>
/// <remarks>
/// This method uses a cache to limit the number of request to the token service.
/// A fresh token can be re-used during its lifetime of 10 minutes. After a successful
/// request to the token service, this method caches the access token. Subsequent
/// invocations of the method return the cached token for the next 8 minutes. After
/// 8 minutes, a new token is fetched from the token service and the cache is updated.
/// </remarks>
public async Task<string> GetAccessTokenAsync()
{
if (string.IsNullOrEmpty(_subscriptionKey))
{
throw new ArgumentNullException(nameof(SubscriptionKey), "A subscription key is required. Go to Azure Portal and sign up for Microsoft Translator: https://portal.azure.com/#create/Microsoft.CognitiveServices/apitype/TextTranslation");
}
// Re-use the cached token if there is one.
if ((DateTime.Now - _storedTokenTime) < TokenCacheDuration && !string.IsNullOrWhiteSpace(_storedTokenValue))
{
return _storedTokenValue;
}
using var request = new HttpRequestMessage(HttpMethod.Post, ServiceUrl);
request.Headers.Add(OcpApimSubscriptionKeyHeader, SubscriptionKey);
var response = await client.SendAsync(request).ConfigureAwait(false);
var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
if (!response.IsSuccessStatusCode)
{
var error = JsonSerializer.Deserialize<ErrorResponse>(content);
throw new TranslatorServiceException(error?.Error?.Message);
}
_storedTokenTime = DateTime.Now;
_storedTokenValue = $"Bearer {content}";
return _storedTokenValue;
}
}
}

Просмотреть файл

@ -1,41 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Collections.Generic;
namespace Microsoft.Toolkit.Services.MicrosoftTranslator
{
/// <summary>
/// Strong type for Detected Language
/// </summary>
/// <seealso cref="ITranslatorService.DetectLanguageWithResponseAsync(string)"/>
/// <seealso cref="ITranslatorService.DetectLanguagesWithResponseAsync(IEnumerable{string})"/>
public class DetectedLanguage : DetectedLanguageBase
{
/// <summary>
/// Gets a value indicating whether the detected language is one of the languages supported for text translation.
/// </summary>
public bool IsTranslationSupported { get; }
/// <summary>
/// Gets a value indicating whether the detected language is one of the languages supported for transliteration.
/// </summary>
public bool IsTransliterationSupported { get; }
/// <summary>
/// Initializes a new instance of the <see cref="DetectedLanguage"/> class.
/// </summary>
/// <param name="language">The code of the detected language.</param>
/// <param name="score">A float value indicating the confidence in the result. The score is between zero and one and a low score indicates a low confidence.</param>
/// <param name="isTranslationSupported">A value indicating whether the detected language is one of the languages supported for text translation.</param>
/// <param name="isTransliterationSupported">A value indicating whether the detected language is one of the languages supported for transliteration.</param>
/// <seealso cref="DetectedLanguageBase"/>
public DetectedLanguage(string language, float score, bool isTranslationSupported, bool isTransliterationSupported)
: base(language, score)
{
IsTranslationSupported = isTranslationSupported;
IsTransliterationSupported = isTransliterationSupported;
}
}
}

Просмотреть файл

@ -1,48 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Collections.Generic;
namespace Microsoft.Toolkit.Services.MicrosoftTranslator
{
/// <summary>
/// Strong type for Base Detected Language
/// </summary>
/// <seealso cref="ITranslatorService.DetectLanguageWithResponseAsync(string)"/>
/// <seealso cref="ITranslatorService.DetectLanguagesWithResponseAsync(IEnumerable{string})"/>
/// <seealso cref="ITranslatorService.TranslateWithResponseAsync(IEnumerable{string}, IEnumerable{string})"/>
/// <seealso cref="ITranslatorService.TranslateWithResponseAsync(IEnumerable{string}, string, IEnumerable{string})"/>
/// <seealso cref="ITranslatorService.TranslateWithResponseAsync(IEnumerable{string}, string, string)"/>
/// <seealso cref="ITranslatorService.TranslateWithResponseAsync(string, IEnumerable{string})"/>
/// <seealso cref="ITranslatorService.TranslateWithResponseAsync(string, string)"/>
/// <seealso cref="ITranslatorService.TranslateWithResponseAsync(string, string, IEnumerable{string})"/>
/// <seealso cref="ITranslatorService.TranslateWithResponseAsync(string, string, string)"/>
public class DetectedLanguageBase
{
/// <summary>
/// Gets the code of the detected language.
/// </summary>
public string Language { get; }
/// <summary>
/// Gets a float value indicating the confidence in the result. The score is between zero and one and a low score indicates a low confidence.
/// </summary>
public float Score { get; }
/// <inheritdoc/>
public override string ToString() => Language;
/// <summary>
/// Initializes a new instance of the <see cref="DetectedLanguageBase"/> class.
/// Returns the language friendly name.
/// </summary>
/// <param name="language">the code of the detected language.</param>
/// <param name="score">a float value indicating the confidence in the result. The score is between zero and one and a low score indicates a low confidence.</param>
public DetectedLanguageBase(string language, float score)
{
Language = language;
Score = score;
}
}
}

Просмотреть файл

@ -1,36 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Collections.Generic;
namespace Microsoft.Toolkit.Services.MicrosoftTranslator
{
/// <summary>
/// Strong type for Detect Language Response
/// </summary>
/// <seealso cref="ITranslatorService.DetectLanguageWithResponseAsync(string)"/>
/// <seealso cref="ITranslatorService.DetectLanguagesWithResponseAsync(IEnumerable{string})"/>
public class DetectedLanguageResponse : DetectedLanguage
{
/// <summary>
/// Gets an array of other possible languages.
/// </summary>
public IEnumerable<DetectedLanguage> Alternatives { get; }
/// <summary>
/// Initializes a new instance of the <see cref="DetectedLanguageResponse"/> class.
/// </summary>
/// <param name="language">The code of the detected language.</param>
/// <param name="score">A float value indicating the confidence in the result. The score is between zero and one and a low score indicates a low confidence.</param>
/// <param name="isTranslationSupported">A value indicating whether the detected language is one of the languages supported for text translation.</param>
/// <param name="isTransliterationSupported">A value indicating whether the detected language is one of the languages supported for transliteration.</param>
/// <param name="alternatives">An array of other possible languages</param>
/// <seealso cref="DetectedLanguage"/>
public DetectedLanguageResponse(string language, float score, bool isTranslationSupported, bool isTransliterationSupported, IEnumerable<DetectedLanguage> alternatives)
: base(language, score, isTranslationSupported, isTransliterationSupported)
{
Alternatives = alternatives;
}
}
}

Просмотреть файл

@ -1,28 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Text.Json.Serialization;
namespace Microsoft.Toolkit.Services.MicrosoftTranslator
{
#pragma warning disable SA1402 // File may only contain a single type
/// <summary>
/// Holds information about an error occurred while accessing Microsoft Translator Service.
/// </summary>
internal class ErrorResponse
{
[JsonPropertyName("error")]
public Error Error { get; set; }
}
internal class Error
{
/// <summary>
/// Gets or sets the error message.
/// </summary>
[JsonPropertyName("message")]
public string Message { get; set; }
}
#pragma warning restore SA1402 // File may only contain a single type
}

Просмотреть файл

@ -1,372 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Microsoft.Toolkit.Services.MicrosoftTranslator
{
/// <summary>
/// The <strong>ITranslatorServiceClient</strong> interface specifies properties and methods to translate text in various supported languages.
/// </summary>
public interface ITranslatorService
{
/// <summary>
/// Gets or sets the Subscription key that is necessary to use <strong>Microsoft Translator Service</strong>.
/// </summary>
/// <value>The Subscription Key.</value>
/// <remarks>
/// <para>You must register Microsoft Translator on https://portal.azure.com/#create/Microsoft.CognitiveServices/apitype/TextTranslation to obtain the Subscription key needed to use the service.</para>
/// </remarks>
string SubscriptionKey { get; set; }
/// <summary>
/// Gets or sets the string representing the supported language code to translate the text in.
/// </summary>
/// <value>The string representing the supported language code to translate the text in. The code must be present in the list of codes returned from the method <see cref="GetLanguagesAsync"/>.</value>
/// <seealso cref="GetLanguagesAsync"/>
string Language { get; set; }
/// <summary>
/// Initializes the <see cref="TranslatorService"/> class by getting an access token for the service.
/// </summary>
/// <returns>A <see cref="Task"/> that represents the initialize operation.</returns>
/// <exception cref="ArgumentNullException">The <see cref="SubscriptionKey"/> property hasn't been set.</exception>
/// <exception cref="TranslatorServiceException">The provided <see cref="SubscriptionKey"/> isn't valid or has expired.</exception>
/// <remarks>Calling this method isn't mandatory, because the token is get/refreshed every time is needed. However, it is called at startup, it can speed-up subsequent requests.</remarks>
Task InitializeAsync();
/// <summary>
/// Initializes the <see cref="TranslatorService"/> class by getting an access token for the service.
/// </summary>
/// <param name="subscriptionKey">The subscription key for the Microsoft Translator Service on Azure.</param>
/// <param name="language">A string representing the supported language code to speak the text in. The code must be present in the list of codes returned from the method <see cref="GetLanguagesAsync"/>.</param>
/// <returns>A <see cref="Task"/> that represents the initialize operation.</returns>
/// <exception cref="ArgumentNullException">The <see cref="SubscriptionKey"/> property hasn't been set.</exception>
/// <exception cref="TranslatorServiceException">The provided <see cref="SubscriptionKey"/> isn't valid or has expired.</exception>
/// <remarks>
/// <para>Calling this method isn't mandatory, because the token is get/refreshed every time is needed. However, it is called at startup, it can speed-up subsequent requests.</para>
/// <para>You must register Microsoft Translator on https://portal.azure.com to obtain the Subscription key needed to use the service.</para>
/// </remarks>
Task InitializeAsync(string subscriptionKey, string language = null);
/// <summary>
/// Detects the language of a text.
/// </summary>
/// <param name="input">A string representing the text whose language must be detected.</param>
/// <returns>A string containing a two-character Language code for the given text.</returns>
/// <exception cref="ArgumentNullException">
/// <list type="bullet">
/// <term>The <see cref="SubscriptionKey"/> property hasn't been set.</term>
/// <term>The <paramref name="input"/> parameter is <strong>null</strong> (<strong>Nothing</strong> in Visual Basic) or empty.</term>
/// </list>
/// </exception>
/// <exception cref="TranslatorServiceException">The provided <see cref="SubscriptionKey"/> isn't valid or has expired.</exception>
/// <remarks><para>This method performs a non-blocking request for language detection.</para>
/// <para>For more information, go to https://docs.microsoft.com/azure/cognitive-services/translator/reference/v3-0-detect.
/// </para></remarks>
/// <seealso cref="GetLanguagesAsync"/>
/// <seealso cref="Language"/>
Task<string> DetectLanguageAsync(string input);
/// <summary>
/// Detects the language of a text.
/// </summary>
/// <param name="input">A string representing the text whose language must be detected.</param>
/// <returns>A <see cref="DetectedLanguageResponse"/> object containing information about the detected language.</returns>
/// <exception cref="ArgumentNullException">
/// <list type="bullet">
/// <term>The <see cref="SubscriptionKey"/> property hasn't been set.</term>
/// <term>The <paramref name="input"/> parameter is <strong>null</strong> (<strong>Nothing</strong> in Visual Basic) or empty.</term>
/// </list>
/// </exception>
/// <exception cref="TranslatorServiceException">The provided <see cref="SubscriptionKey"/> isn't valid or has expired.</exception>
/// <remarks><para>This method performs a non-blocking request for language detection.</para>
/// <para>For more information, go to https://docs.microsoft.com/azure/cognitive-services/translator/reference/v3-0-detect.
/// </para></remarks>
/// <seealso cref="GetLanguagesAsync"/>
/// <seealso cref="Language"/>
Task<DetectedLanguageResponse> DetectLanguageWithResponseAsync(string input);
/// <summary>
/// Detects the language of a text.
/// </summary>
/// <param name="input">A string array containing the sentences whose language must be detected.</param>
/// <returns>A <see cref="DetectedLanguageResponse"/> array with one result for each string in the input array. Each object contains information about the detected language.</returns>
/// <exception cref="ArgumentException">
/// <list type="bullet">
/// <term>The <paramref name="input"/> parameter doesn't contain any element.</term>
/// <term>The <paramref name="input"/> array contains more than 100 elements.</term>
/// </list>
/// </exception>
/// <exception cref="ArgumentNullException">
/// <list type="bullet">
/// <term>The <see cref="SubscriptionKey"/> property hasn't been set.</term>
/// <term>The <paramref name="input"/> array is <strong>null</strong> (<strong>Nothing</strong> in Visual Basic).</term>
/// </list>
/// </exception>
/// <exception cref="TranslatorServiceException">The provided <see cref="SubscriptionKey"/> isn't valid or has expired.</exception>
/// <remarks><para>This method performs a non-blocking request for language detection.</para>
/// <para>For more information, go to https://docs.microsoft.com/azure/cognitive-services/translator/reference/v3-0-detect.
/// </para></remarks>
/// <seealso cref="GetLanguagesAsync"/>
/// <seealso cref="Language"/>
Task<IEnumerable<DetectedLanguageResponse>> DetectLanguagesWithResponseAsync(IEnumerable<string> input);
/// <summary>
/// Retrieves the languages available for translation.
/// </summary>
/// <returns>A string array containing the language codes supported for translation by <strong>Microsoft Translator Service</strong>.</returns> /// <exception cref="ArgumentNullException">The <see cref="SubscriptionKey"/> property hasn't been set.</exception>
/// <exception cref="TranslatorServiceException">The provided <see cref="SubscriptionKey"/> isn't valid or has expired.</exception>
/// <remarks><para>This method performs a non-blocking request for language codes.</para>
/// <para>For more information, go to https://docs.microsoft.com/azure/cognitive-services/translator/reference/v3-0-languages.
/// </para>
/// </remarks>
/// <seealso cref="GetLanguageNamesAsync(string)"/>
Task<IEnumerable<string>> GetLanguagesAsync();
/// <summary>
/// Retrieves friendly names for the languages available for text translation.
/// </summary>
/// <param name="language">The language used to localize the language names. If the parameter is set to <strong>null</strong>, the language specified in the <seealso cref="Language"/> property will be used.</param>
/// <returns>An array of <see cref="ServiceLanguage"/> containing the language codes and names supported for translation by <strong>Microsoft Translator Service</strong>.</returns>
/// <exception cref="ArgumentNullException">The <see cref="SubscriptionKey"/> property hasn't been set.</exception>
/// <exception cref="TranslatorServiceException">The provided <see cref="SubscriptionKey"/> isn't valid or has expired.</exception>
/// <remarks><para>This method performs a non-blocking request for language names.</para>
/// <para>For more information, go to https://docs.microsoft.com/azure/cognitive-services/translator/reference/v3-0-languages.
/// </para>
/// </remarks>
/// <seealso cref="GetLanguagesAsync"/>
Task<IEnumerable<ServiceLanguage>> GetLanguageNamesAsync(string language = null);
/// <summary>
/// Translates a text string into the specified language.
/// </summary>
/// <returns>A string representing the translated text.</returns>
/// <param name="input">A string representing the text to translate.</param>
/// <param name="to">A string representing the language code to translate the text into. The code must be present in the list of codes returned from the <see cref="GetLanguagesAsync"/> method. If the parameter is set to <strong>null</strong>, the language specified in the <seealso cref="Language"/> property will be used.</param>
/// <exception cref="ArgumentNullException">
/// <list type="bullet">
/// <term>The <see cref="SubscriptionKey"/> property hasn't been set.</term>
/// <term>The <paramref name="input"/> parameter is <strong>null</strong> (<strong>Nothing</strong> in Visual Basic) or empty.</term>
/// </list>
/// </exception>
/// <exception cref="ArgumentException">The <paramref name="input"/> parameter is longer than 1000 characters.</exception>
/// <exception cref="TranslatorServiceException">The provided <see cref="SubscriptionKey"/> isn't valid or has expired.</exception>
/// <remarks><para>This method perform a non-blocking request for text translation.</para>
/// <para>For more information, go to https://docs.microsoft.com/azure/cognitive-services/translator/reference/v3-0-translate.
/// </para>
/// </remarks>
/// <seealso cref="Language"/>
/// <seealso cref="GetLanguagesAsync"/>
Task<string> TranslateAsync(string input, string to = null);
/// <summary>
/// Translates a text string into the specified language.
/// </summary>
/// <returns>A string representing the translated text.</returns>
/// <param name="input">A string representing the text to translate.</param>
/// <param name="from">A string representing the language code of the original text. The code must be present in the list of codes returned from the <see cref="GetLanguagesAsync"/> method. If the parameter is set to <strong>null</strong>, the language specified in the <seealso cref="Language"/> property will be used.</param>
/// <param name="to">A string representing the language code to translate the text into. The code must be present in the list of codes returned from the <see cref="GetLanguagesAsync"/> method. If the parameter is set to <strong>null</strong>, the language specified in the <seealso cref="Language"/> property will be used.</param>
/// <exception cref="ArgumentNullException">
/// <list type="bullet">
/// <term>The <see cref="SubscriptionKey"/> property hasn't been set.</term>
/// <term>The <paramref name="input"/> parameter is <strong>null</strong> (<strong>Nothing</strong> in Visual Basic) or empty.</term>
/// </list>
/// </exception>
/// <exception cref="ArgumentException">The <paramref name="input"/> parameter is longer than 1000 characters.</exception>
/// <exception cref="TranslatorServiceException">The provided <see cref="SubscriptionKey"/> isn't valid or has expired.</exception>
/// <remarks><para>This method perform a non-blocking request for text translation.</para>
/// <para>For more information, go to https://docs.microsoft.com/azure/cognitive-services/translator/reference/v3-0-translate.
/// </para>
/// </remarks>
/// <seealso cref="Language"/>
/// <seealso cref="GetLanguagesAsync"/>
Task<string> TranslateAsync(string input, string from, string to);
/// <summary>
/// Translates a text string into the specified language.
/// </summary>
/// <returns>A <see cref="TranslationResponse"/> object containing translated text and information.</returns>
/// <param name="input">A string representing the text to translate.</param>
/// <param name="to">A string representing the language code to translate the text into. The code must be present in the list of codes returned from the <see cref="GetLanguagesAsync"/> method. If the parameter is set to <strong>null</strong>, the language specified in the <seealso cref="Language"/> property will be used.</param>
/// <exception cref="ArgumentNullException">
/// <list type="bullet">
/// <term>The <see cref="SubscriptionKey"/> property hasn't been set.</term>
/// <term>The <paramref name="input"/> parameter is <strong>null</strong> (<strong>Nothing</strong> in Visual Basic) or empty.</term>
/// </list>
/// </exception>
/// <exception cref="ArgumentException">The <paramref name="input"/> parameter is longer than 1000 characters.</exception>
/// <exception cref="TranslatorServiceException">The provided <see cref="SubscriptionKey"/> isn't valid or has expired.</exception>
/// <remarks><para>This method perform a non-blocking request for text translation.</para>
/// <para>For more information, go to https://docs.microsoft.com/azure/cognitive-services/translator/reference/v3-0-translate.
/// </para>
/// </remarks>
/// <seealso cref="Language"/>
/// <seealso cref="GetLanguagesAsync"/>
Task<TranslationResponse> TranslateWithResponseAsync(string input, string to = null);
/// <summary>
/// Translates a text string into the specified languages.
/// </summary>
/// <returns>A <see cref="TranslationResponse"/> object containing translated text and information.</returns>
/// <param name="input">A string representing the text to translate.</param>
/// <param name="from">A string representing the language code of the original text. The code must be present in the list of codes returned from the <see cref="GetLanguagesAsync"/> method. If the parameter is set to <strong>null</strong>, the language specified in the <seealso cref="Language"/> property will be used.</param>
/// <param name="to">A string representing the language code to translate the text into. The code must be present in the list of codes returned from the <see cref="GetLanguagesAsync"/> method. If the parameter is set to <strong>null</strong>, the language specified in the <seealso cref="Language"/> property will be used.</param>
/// <exception cref="ArgumentNullException">
/// <list type="bullet">
/// <term>The <see cref="SubscriptionKey"/> property hasn't been set.</term>
/// <term>The <paramref name="input"/> parameter is <strong>null</strong> (<strong>Nothing</strong> in Visual Basic) or empty.</term>
/// </list>
/// </exception>
/// <exception cref="ArgumentException">The <paramref name="input"/> parameter is longer than 1000 characters.</exception>
/// <exception cref="TranslatorServiceException">The provided <see cref="SubscriptionKey"/> isn't valid or has expired.</exception>
/// <remarks><para>This method perform a non-blocking request for text translation.</para>
/// <para>For more information, go to https://docs.microsoft.com/azure/cognitive-services/translator/reference/v3-0-translate.
/// </para>
/// </remarks>
/// <seealso cref="Language"/>
/// <seealso cref="GetLanguagesAsync"/>
Task<TranslationResponse> TranslateWithResponseAsync(string input, string from, string to);
/// <summary>
/// Translates a list of sentences into the specified language.
/// </summary>
/// <returns>A <see cref="TranslationResponse"/> array with one result for each language code in the <paramref name="to"/> array. Each object contains translated text and information.</returns>
/// <param name="input">A string array containing the sentences to translate.</param>
/// <param name="from">A string representing the language code of the original text. The code must be present in the list of codes returned from the <see cref="GetLanguagesAsync"/> method. If the parameter is set to <strong>null</strong>, the language specified in the <seealso cref="Language"/> property will be used.</param>
/// <param name="to">A string representing the language code to translate the text into. The code must be present in the list of codes returned from the <see cref="GetLanguagesAsync"/> method. If the parameter is set to <strong>null</strong>, the language specified in the <seealso cref="Language"/> property will be used.</param>
/// <exception cref="ArgumentNullException">
/// <list type="bullet">
/// <term>The <see cref="SubscriptionKey"/> property hasn't been set.</term>
/// <term>The <paramref name="input"/> parameter is <strong>null</strong> (<strong>Nothing</strong> in Visual Basic).</term>
/// </list>
/// </exception>
/// <exception cref="ArgumentException">
/// <list type="bullet">
/// <term>The <paramref name="input"/> parameter is longer than 1000 characters.</term>
/// <term>The <paramref name="input"/> array contains more than 25 elements.</term>
/// </list>
/// </exception>
/// <exception cref="TranslatorServiceException">The provided <see cref="SubscriptionKey"/> isn't valid or has expired.</exception>
/// <remarks><para>This method perform a non-blocking request for text translation.</para>
/// <para>For more information, go to https://docs.microsoft.com/azure/cognitive-services/translator/reference/v3-0-translate.
/// </para>
/// </remarks>
/// <seealso cref="Language"/>
/// <seealso cref="GetLanguagesAsync"/>
Task<IEnumerable<TranslationResponse>> TranslateWithResponseAsync(IEnumerable<string> input, string from, string to);
/// <summary>
/// Translates a text into the specified languages.
/// </summary>
/// <returns>A <see cref="TranslationResponse"/> object containing translated text and information.</returns>
/// <param name="input">A string representing the text to translate.</param>
/// <param name="from">A string representing the language code of the original text. The code must be present in the list of codes returned from the <see cref="GetLanguagesAsync"/> method. If the parameter is set to <strong>null</strong>, the language specified in the <seealso cref="Language"/> property will be used.</param>
/// <param name="to">A string array representing the language codes to translate the text into. The code must be present in the list of codes returned from the <see cref="GetLanguagesAsync"/> method. If the parameter is set to <strong>null</strong>, the language specified in the <seealso cref="Language"/> property will be used.</param>
/// <exception cref="ArgumentNullException">
/// <list type="bullet">
/// <term>The <see cref="SubscriptionKey"/> property hasn't been set.</term>
/// <term>The <paramref name="input"/> parameter is <strong>null</strong> (<strong>Nothing</strong> in Visual Basic) or empty.</term>
/// </list>
/// </exception>
/// <exception cref="ArgumentException">
/// <list type="bullet">
/// <term>The <paramref name="input"/> parameter is longer than 1000 characters.</term>
/// <term>The <paramref name="to"/> array contains more than 25 elements.</term>
/// </list>
/// </exception>
/// <exception cref="TranslatorServiceException">The provided <see cref="SubscriptionKey"/> isn't valid or has expired.</exception>
/// <remarks><para>This method perform a non-blocking request for text translation.</para>
/// <para>For more information, go to https://docs.microsoft.com/azure/cognitive-services/translator/reference/v3-0-translate.
/// </para>
/// </remarks>
/// <seealso cref="Language"/>
/// <seealso cref="GetLanguagesAsync"/>
Task<TranslationResponse> TranslateWithResponseAsync(string input, string from, IEnumerable<string> to);
/// <summary>
/// Translates a text string into the specified languages.
/// </summary>
/// <returns>A <see cref="TranslationResponse"/> object containing translated text and information.</returns>
/// <param name="input">A string representing the text to translate.</param>
/// <param name="to">A string array representing the language codes to translate the text into. The code must be present in the list of codes returned from the <see cref="GetLanguagesAsync"/> method. If the parameter is set to <strong>null</strong>, the language specified in the <seealso cref="Language"/> property will be used.</param>
/// <exception cref="ArgumentNullException">
/// <list type="bullet">
/// <term>The <see cref="SubscriptionKey"/> property hasn't been set.</term>
/// <term>The <paramref name="input"/> parameter is <strong>null</strong> (<strong>Nothing</strong> in Visual Basic) or empty.</term>
/// </list>
/// </exception>
/// <exception cref="ArgumentException">
/// <list type="bullet">
/// <term>The <paramref name="input"/> parameter is longer than 1000 characters.</term>
/// <term>The <paramref name="to"/> array contains more than 25 elements.</term>
/// </list>
/// </exception>
/// <exception cref="TranslatorServiceException">The provided <see cref="SubscriptionKey"/> isn't valid or has expired.</exception>
/// <remarks><para>This method perform a non-blocking request for text translation.</para>
/// <para>For more information, go to https://docs.microsoft.com/azure/cognitive-services/translator/reference/v3-0-translate.
/// </para>
/// </remarks>
/// <seealso cref="Language"/>
/// <seealso cref="GetLanguagesAsync"/>
Task<TranslationResponse> TranslateWithResponseAsync(string input, IEnumerable<string> to);
/// <summary>
/// Translates a list of sentences into the specified languages.
/// </summary>
/// <returns>A <see cref="TranslationResponse"/> array with one result for each language code in the <paramref name="to"/> array. Each object contains translated text and information.</returns>
/// <param name="input">A string array containing the sentences to translate.</param>
/// <param name="to">A string array representing the language codes to translate the text into. The code must be present in the list of codes returned from the <see cref="GetLanguagesAsync"/> method. If the parameter is set to <strong>null</strong>, the language specified in the <seealso cref="Language"/> property will be used.</param>
/// <exception cref="ArgumentNullException">
/// <list type="bullet">
/// <term>The <see cref="SubscriptionKey"/> property hasn't been set.</term>
/// <term>The <paramref name="input"/> parameter is <strong>null</strong> (<strong>Nothing</strong> in Visual Basic).</term>
/// </list>
/// </exception>
/// <exception cref="ArgumentException">
/// <list type="bullet">
/// <term>The <paramref name="input"/> parameter is longer than 1000 characters.</term>
/// <term>The <paramref name="input"/> array contains more than 25 elements.</term>
/// </list>
/// </exception>
/// <exception cref="TranslatorServiceException">The provided <see cref="SubscriptionKey"/> isn't valid or has expired.</exception>
/// <remarks><para>This method perform a non-blocking request for text translation.</para>
/// <para>For more information, go to https://docs.microsoft.com/azure/cognitive-services/translator/reference/v3-0-translate.
/// </para>
/// </remarks>
/// <seealso cref="Language"/>
/// <seealso cref="GetLanguagesAsync"/>
Task<IEnumerable<TranslationResponse>> TranslateWithResponseAsync(IEnumerable<string> input, IEnumerable<string> to = null);
/// <summary>
/// Translates a list of sentences into the specified languages.
/// </summary>
/// <returns>A <see cref="TranslationResponse"/> array with one result for each language code in the <paramref name="to"/> array. Each object contains translated text and information.</returns>
/// <param name="input">A string array containing the sentences to translate.</param>
/// <param name="from">A string representing the language code of the original text. The code must be present in the list of codes returned from the <see cref="GetLanguagesAsync"/> method. If the parameter is set to <strong>null</strong>, the language specified in the <seealso cref="Language"/> property will be used.</param>
/// <param name="to">A string array representing the language codes to translate the text into. The code must be present in the list of codes returned from the <see cref="GetLanguagesAsync"/> method. If the parameter is set to <strong>null</strong>, the language specified in the <seealso cref="Language"/> property will be used.</param>
/// <exception cref="ArgumentNullException">
/// <list type="bullet">
/// <term>The <see cref="SubscriptionKey"/> property hasn't been set.</term>
/// <term>The <paramref name="input"/> parameter is <strong>null</strong> (<strong>Nothing</strong> in Visual Basic).</term>
/// </list>
/// </exception>
/// <exception cref="ArgumentException">
/// <list type="bullet">
/// <term>The <paramref name="input"/> parameter is longer than 1000 characters.</term>
/// <term>The <paramref name="input"/> array contains more than 25 elements.</term>
/// </list>
/// </exception>
/// <exception cref="TranslatorServiceException">The provided <see cref="SubscriptionKey"/> isn't valid or has expired.</exception>
/// <remarks><para>This method perform a non-blocking request for text translation.</para>
/// <para>For more information, go to https://docs.microsoft.com/azure/cognitive-services/translator/reference/v3-0-translate.
/// </para>
/// </remarks>
/// <seealso cref="Language"/>
/// <seealso cref="GetLanguagesAsync"/>
Task<IEnumerable<TranslationResponse>> TranslateWithResponseAsync(IEnumerable<string> input, string from, IEnumerable<string> to);
}
}

Просмотреть файл

@ -1,58 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Text.Json.Serialization;
namespace Microsoft.Toolkit.Services.MicrosoftTranslator
{
/// <summary>
/// Holds information about languages supported for text translation and speech synthesis.
/// </summary>
/// <seealso cref="ITranslatorService.GetLanguageNamesAsync(string)"/>
public class ServiceLanguage
{
/// <summary>
/// Gets the language code.
/// </summary>
public string Code { get; internal set; }
/// <summary>
/// Gets the language friendly name.
/// </summary>
public string Name { get; }
/// <summary>
/// Gets the display name of the language in the locale native for this language.
/// </summary>
public string NativeName { get; }
/// <summary>
/// Gets the directionality, which is rtl for right-to-left languages or ltr for left-to-right languages.
/// </summary>
[JsonPropertyName("dir")]
public string Directionality { get; }
/// <summary>
/// Returns the language friendly name.
/// </summary>
/// <returns>The language friendly name.</returns>
public override string ToString() => Name;
/// <summary>
/// Initializes a new instance of the <see cref="ServiceLanguage"/> class.
/// Returns the language friendly name.
/// </summary>
/// <param name="code">The language code.</param>
/// <param name="name">The language friendly name.</param>
/// <param name="nativeName">The display name of the language in the locale native for this language.</param>
/// <param name="directionality">The directionality, which is rtl for right-to-left languages or ltr for left-to-right languages</param>
public ServiceLanguage(string code, string name, string nativeName = null, string directionality = null)
{
Code = code;
Name = name;
NativeName = nativeName ?? name;
Directionality = directionality ?? "ltr";
}
}
}

Просмотреть файл

@ -1,34 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace Microsoft.Toolkit.Services.MicrosoftTranslator
{
/// <summary>
/// Strong type for Translation
/// </summary>
/// <seealso cref="ITranslatorService.TranslateAsync(string, string, string)"/>
public class Translation
{
/// <summary>
/// Gets a string giving the translated text.
/// </summary>
public string Text { get; }
/// <summary>
/// Gets a string representing the language code of the target language.
/// </summary>
public string To { get; }
/// <summary>
/// Initializes a new instance of the <see cref="Translation"/> class.
/// </summary>
/// <param name="text">A string giving the translated text.</param>
/// <param name="to">a string representing the language code of the target language.</param>
public Translation(string text, string to)
{
Text = text;
To = to;
}
}
}

Просмотреть файл

@ -1,46 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Collections.Generic;
using System.Linq;
namespace Microsoft.Toolkit.Services.MicrosoftTranslator
{
/// <summary>
/// Strong type for Translate Response
/// </summary>
/// <seealso cref="ITranslatorService.TranslateAsync(string, string, string)"/>
public class TranslationResponse
{
/// <summary>
/// Gets a <see cref="DetectedLanguageBase"/> object describing the detected language.
/// </summary>
/// <remarks>This property has a value only when the <see cref="ITranslatorService.TranslateWithResponseAsync(string, string)"/> method is invoked without the <strong>from</strong> parameter, so that automatic language detection is applied to determine the source language.
/// </remarks>
public DetectedLanguageBase DetectedLanguage { get; }
/// <summary>
/// Gets an array of <see cref="MicrosoftTranslator.Translation"/> results.
/// </summary>
public IEnumerable<Translation> Translations { get; }
/// <summary>
/// Gets the first translation result.
/// </summary>
public Translation Translation => Translations?.FirstOrDefault();
/// <summary>
/// Initializes a new instance of the <see cref="TranslationResponse"/> class.
/// </summary>
/// <param name="detectedLanguage">A <see cref="DetectedLanguageBase"/> object describing the detected language.</param>
/// <param name="translations">an array of <see cref="MicrosoftTranslator.Translation"/> results.</param>
/// <seealso cref="MicrosoftTranslator.Translation"/>
/// <seealso cref="DetectedLanguageBase"/>
public TranslationResponse(DetectedLanguageBase detectedLanguage, IEnumerable<Translation> translations)
{
DetectedLanguage = detectedLanguage;
Translations = translations;
}
}
}

Просмотреть файл

@ -1,261 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text.Json;
using System.Threading.Tasks;
namespace Microsoft.Toolkit.Services.MicrosoftTranslator
{
/// <summary>
/// The <strong>TranslatorService</strong> class provides methods to translate text in various supported languages.
/// </summary>
/// <remarks>
/// <para>To use this library, you must register Microsoft Translator on https://portal.azure.com/#create/Microsoft.CognitiveServices/apitype/TextTranslation to obtain the Subscription key.
/// </para>
/// </remarks>
public class TranslatorService : ITranslatorService
{
private const string BaseUrl = "https://api.cognitive.microsofttranslator.com/";
private const string ApiVersion = "api-version=3.0";
private const string AuthorizationUri = "Authorization";
private const string JsonMediaType = "application/json";
private const int _MaxArrayLengthForTranslation = 25;
private const int _MaxTextLengthForTranslation = 5000;
private const int _MaxArrayLengthForDetection = 100;
private const int _MaxTextLengthForDetection = 10000;
private static HttpClient client = new HttpClient();
/// <summary>
/// Private singleton field.
/// </summary>
private static TranslatorService instance;
/// <summary>
/// Gets public singleton property.
/// </summary>
public static TranslatorService Instance => instance ?? (instance = new TranslatorService());
private AzureAuthToken _authToken;
private string _authorizationHeaderValue = string.Empty;
/// <summary>
/// Gets a reference to an instance of the underlying data provider.
/// </summary>
public object Provider
{
get { throw new NotImplementedException(); }
}
private TranslatorService()
{
_authToken = new AzureAuthToken(string.Empty);
Language = CultureInfo.CurrentCulture.Name.ToLower();
}
/// <inheritdoc/>
public string SubscriptionKey
{
get { return _authToken.SubscriptionKey; }
set { _authToken.SubscriptionKey = value; }
}
/// <inheritdoc/>
public string Language { get; set; }
/// <inheritdoc/>
public async Task<string> DetectLanguageAsync(string input)
{
var response = await DetectLanguageWithResponseAsync(input).ConfigureAwait(false);
return response?.Language;
}
/// <inheritdoc/>
public async Task<DetectedLanguageResponse> DetectLanguageWithResponseAsync(string input)
{
var response = await DetectLanguagesWithResponseAsync(new string[] { input }).ConfigureAwait(false);
return response.FirstOrDefault();
}
/// <inheritdoc/>
public async Task<IEnumerable<DetectedLanguageResponse>> DetectLanguagesWithResponseAsync(IEnumerable<string> input)
{
if (input == null)
{
throw new ArgumentNullException(nameof(input));
}
if (!input.Any())
{
throw new ArgumentException($"{nameof(input)} array must contain at least 1 element");
}
if (input.Count() > _MaxArrayLengthForDetection)
{
throw new ArgumentException($"{nameof(input)} array can have at most {_MaxArrayLengthForDetection} elements");
}
// Checks if it is necessary to obtain/update access token.
await CheckUpdateTokenAsync().ConfigureAwait(false);
var uriString = $"{BaseUrl}detect?{ApiVersion}";
using var request = CreateHttpRequest(uriString, HttpMethod.Post, input.Select(t => new { Text = t.Substring(0, Math.Min(t.Length, _MaxTextLengthForDetection)) }));
var response = await client.SendAsync(request).ConfigureAwait(false);
var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
var responseContent = JsonSerializer.Deserialize<IEnumerable<DetectedLanguageResponse>>(content);
return responseContent;
}
/// <inheritdoc/>
public async Task<IEnumerable<string>> GetLanguagesAsync()
{
var languages = await GetLanguageNamesAsync();
return languages.OrderBy(l => l.Code).Select(l => l.Code).ToList();
}
/// <inheritdoc/>
public async Task<IEnumerable<ServiceLanguage>> GetLanguageNamesAsync(string language = null)
{
// Check if it is necessary to obtain/update access token.
await CheckUpdateTokenAsync().ConfigureAwait(false);
var uriString = $"{BaseUrl}languages?scope=translation&{ApiVersion}";
using var request = CreateHttpRequest(uriString);
language = language ?? Language;
if (!string.IsNullOrWhiteSpace(language))
{
// If necessary, adds the Accept-Language header in order to get localized language names.
request.Headers.AcceptLanguage.Add(new StringWithQualityHeaderValue(language));
}
var response = await client.SendAsync(request).ConfigureAwait(false);
var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
var jsonContent = JsonDocument.Parse(content).RootElement.GetProperty("translation");
var responseContent = JsonSerializer.Deserialize<Dictionary<string, ServiceLanguage>>(jsonContent.ToString()).ToList();
responseContent.ForEach(r => r.Value.Code = r.Key);
return responseContent.Select(r => r.Value).OrderBy(r => r.Name).ToList();
}
/// <inheritdoc/>
public Task<string> TranslateAsync(string input, string to = null) => TranslateAsync(input, null, to ?? Language);
/// <inheritdoc/>
public async Task<string> TranslateAsync(string input, string from, string to)
{
var response = await TranslateWithResponseAsync(new string[] { input }, from, new string[] { to }).ConfigureAwait(false);
return response.FirstOrDefault()?.Translation.Text;
}
/// <inheritdoc/>
public Task<TranslationResponse> TranslateWithResponseAsync(string input, string to = null) => TranslateWithResponseAsync(input, null, to ?? Language);
/// <inheritdoc/>
public async Task<TranslationResponse> TranslateWithResponseAsync(string input, string from, string to)
{
var response = await TranslateWithResponseAsync(new string[] { input }, from, new string[] { to }).ConfigureAwait(false);
return response.FirstOrDefault();
}
/// <inheritdoc/>
public Task<IEnumerable<TranslationResponse>> TranslateWithResponseAsync(IEnumerable<string> input, string from, string to) => TranslateWithResponseAsync(input, from, new string[] { to });
/// <inheritdoc/>
public Task<TranslationResponse> TranslateWithResponseAsync(string input, IEnumerable<string> to) => TranslateWithResponseAsync(input, null, to);
/// <inheritdoc/>
public async Task<TranslationResponse> TranslateWithResponseAsync(string input, string from, IEnumerable<string> to)
{
var response = await TranslateWithResponseAsync(new string[] { input }, from, to).ConfigureAwait(false);
return response.FirstOrDefault();
}
/// <inheritdoc/>
public Task<IEnumerable<TranslationResponse>> TranslateWithResponseAsync(IEnumerable<string> input, IEnumerable<string> to = null) => TranslateWithResponseAsync(input, null, to);
/// <inheritdoc/>
public async Task<IEnumerable<TranslationResponse>> TranslateWithResponseAsync(IEnumerable<string> input, string from, IEnumerable<string> to)
{
if (input == null)
{
throw new ArgumentNullException(nameof(input));
}
if (input.Count() > _MaxArrayLengthForTranslation)
{
throw new ArgumentException($"{nameof(input)} array can have at most {_MaxArrayLengthForTranslation} elements");
}
if (input.Any(str => string.IsNullOrWhiteSpace(str) || str.Length > _MaxTextLengthForTranslation))
{
throw new ArgumentException($"Each sentence cannot be null and longer than {_MaxTextLengthForTranslation} characters");
}
if (to == null || !to.Any())
{
to = new string[] { Language };
}
// Checks if it is necessary to obtain/update access token.
await CheckUpdateTokenAsync().ConfigureAwait(false);
var toQueryString = string.Join("&", to.Select(t => $"to={t}"));
var uriString = (string.IsNullOrWhiteSpace(from) ? $"{BaseUrl}translate?{toQueryString}" : $"{BaseUrl}translate?from={from}&{toQueryString}") + $"&{ApiVersion}";
using var request = CreateHttpRequest(uriString, HttpMethod.Post, input.Select(t => new { Text = t }));
var response = await client.SendAsync(request).ConfigureAwait(false);
var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
var responseContent = JsonSerializer.Deserialize<IEnumerable<TranslationResponse>>(content);
return responseContent;
}
/// <inheritdoc/>
public Task InitializeAsync() => CheckUpdateTokenAsync();
/// <inheritdoc/>
public Task InitializeAsync(string subscriptionKey, string language = null)
{
_authToken = new AzureAuthToken(subscriptionKey);
Language = language ?? CultureInfo.CurrentCulture.Name.ToLower();
return InitializeAsync();
}
private async Task CheckUpdateTokenAsync()
{
// If necessary, updates the access token.
_authorizationHeaderValue = await _authToken.GetAccessTokenAsync().ConfigureAwait(false);
}
private HttpRequestMessage CreateHttpRequest(string uriString)
=> CreateHttpRequest(uriString, HttpMethod.Get);
private HttpRequestMessage CreateHttpRequest(string uriString, HttpMethod method, object content = null)
{
var request = new HttpRequestMessage(method, new Uri(uriString));
request.Headers.Add(AuthorizationUri, _authorizationHeaderValue);
if (content != null)
{
var jsonRequest = JsonSerializer.Serialize(content);
var requestContent = new StringContent(jsonRequest, System.Text.Encoding.UTF8, JsonMediaType);
request.Content = requestContent;
}
return request;
}
}
}

Просмотреть файл

@ -1,24 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
namespace Microsoft.Toolkit.Services.MicrosoftTranslator
{
/// <summary>
/// The <strong>TranslatorServiceException</strong> class holds information about Exception related to <see cref="TranslatorService"/>.
/// </summary>
/// <seealso cref="TranslatorService"/>
public class TranslatorServiceException : Exception
{
/// <summary>
/// Initializes a new instance of the <see cref="TranslatorServiceException"/> class using the specified error message.
/// </summary>
/// <param name="message">Message that describes the error</param>
public TranslatorServiceException(string message)
: base(message)
{
}
}
}

Просмотреть файл

@ -1,13 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace Microsoft.Toolkit.Services.Twitter
{
/// <summary>
/// Any kind of twitter object.
/// </summary>
public interface ITwitterResult
{
}
}

Просмотреть файл

@ -1,213 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Globalization;
using System.Text.Json.Serialization;
namespace Microsoft.Toolkit.Services.Twitter
{
/// <summary>
/// Twitter Timeline item.
/// </summary>
public class Tweet : Toolkit.Parsers.SchemaBase, ITwitterResult
{
private string _text;
/// <summary>
/// Gets or sets time item was created.
/// </summary>
[JsonPropertyName("created_at")]
public string CreatedAt { get; set; }
/// <summary>
/// Gets or sets item Id.
/// </summary>
[JsonPropertyName("id_str")]
public string Id { get; set; }
/// <summary>
/// Gets or sets text of the tweet (handles both 140 and 280 characters)
/// </summary>
[JsonPropertyName("text")]
public string Text
{
get { return _text ?? FullText; }
set { _text = value; }
}
/// <summary>
/// Gets or sets text of the tweet (280 characters).
/// </summary>
[JsonPropertyName("full_text")]
private string FullText { get; set; }
/// <summary>
/// Gets or sets display text range (indexes of tweet text without RT and leading user mentions)
/// </summary>
[JsonPropertyName("display_text_range")]
public int[] DisplayTextRange { get; set; }
/// <summary>
/// Gets or sets a value indicating whether tweet is truncated
/// (true when tweet is longer than 140 characters)
/// This entity may be deprecated - it never seems to be set to true.
/// </summary>
[JsonPropertyName("truncated")]
public bool IsTruncated { get; set; }
/// <summary>
/// Gets or sets attached content of the tweet
/// </summary>
[JsonPropertyName("entities")]
public TwitterEntities Entities { get; set; }
/// <summary>
/// Gets or sets extended attached content of the tweet
/// </summary>
[JsonPropertyName("extended_entities")]
public TwitterExtendedEntities ExtendedEntities { get; set; }
/// <summary>
/// Gets or sets tweet source (client or website used)
/// </summary>
[JsonPropertyName("source")]
public string Source { get; set; }
/// <summary>
/// Gets or sets in_reply_to_screen_name
/// </summary>
[JsonPropertyName("in_reply_to_screen_name")]
public string InReplyToScreenName { get; set; }
/// <summary>
/// Gets or sets in_reply_to_status_id_str
/// </summary>
[JsonPropertyName("in_reply_to_status_id_str")]
public string InReplyToStatusId { get; set; }
/// <summary>
/// Gets or sets in_reply_to_user_id_str
/// </summary>
[JsonPropertyName("in_reply_to_user_id_str")]
public string InReplyToUserId { get; set; }
/// <summary>
/// Gets or sets user who posted the status.
/// </summary>
[JsonPropertyName("user")]
public TwitterUser User { get; set; }
/// <summary>
/// Gets or sets geo coordinates (latitude and longitude) returned by Twitter for some locations
/// </summary>
[JsonPropertyName("coordinates")]
[JsonConverter(typeof(TwitterCoordinatesConverter))]
public TwitterCoordinates Coordinates { get; set; }
/// <summary>
/// Gets or sets the Place object returned by Twitter for some locations
/// </summary>
[JsonPropertyName("place")]
public TwitterPlace Place { get; set; }
/// <summary>
/// Gets or sets the Retweeted Tweet
/// </summary>
[JsonPropertyName("retweeted_status")]
public Tweet RetweetedStatus { get; set; }
/// <summary>
/// Gets the creation date
/// </summary>
public DateTime CreationDate
{
get
{
DateTime dt;
if (!DateTime.TryParseExact(CreatedAt, "ddd MMM dd HH:mm:ss zzzz yyyy", CultureInfo.InvariantCulture, DateTimeStyles.None, out dt))
{
dt = DateTime.Today;
}
return dt;
}
}
/// <summary>
/// Gets or sets quoted_status
/// </summary>
[JsonPropertyName("quoted_status")]
public Tweet QuotedStatus { get; set; }
/// <summary>
/// Gets or sets quoted_status_id_str
/// </summary>
[JsonPropertyName("quoted_status_id_str")]
public string QuotedStatusId { get; set; }
/// <summary>
/// Gets or sets quoted_status_permalink
/// </summary>
[JsonPropertyName("quoted_status_permalink")]
public TwitterUrl QuotedStatusPermalink { get; set; }
/// <summary>
/// Gets or sets approximate count of tweets quoting tweet
/// </summary>
[JsonPropertyName("quote_count")]
public int QuoteCount { get; set; }
/// <summary>
/// Gets or sets number of replies to tweet
/// </summary>
/// <remarks>
/// Premium and Enterprise API access only
/// </remarks>
[JsonPropertyName("reply_count")]
public int ReplyCount { get; set; }
/// <summary>
/// Gets or sets number of times tweet has been retweeted
/// </summary>
[JsonPropertyName("retweet_count")]
public int RetweetCount { get; set; }
/// <summary>
/// Gets or sets number of times tweet has been liked
/// </summary>
[JsonPropertyName("favorite_count")]
public int FavoriteCount { get; set; }
/// <summary>
/// Gets or sets a value indicating whether or not logged-in user has liked tweet
/// </summary>
[JsonPropertyName("favorited")]
public bool Favorited { get; set; }
/// <summary>
/// Gets or sets a value indicating whether or not logged-in user has retweeted tweet
/// </summary>
[JsonPropertyName("retweeted")]
public bool Retweeted { get; set; }
/// <summary>
/// Gets or sets a value indicating whether URL in tweet has been flagged for sensitive content
/// </summary>
[JsonPropertyName("possibly_sensitive")]
public bool Sensitive { get; set; }
/// <summary>
/// Gets or sets stream filter of tweet
/// </summary>
[JsonPropertyName("filter_level")]
public string FilterLevel { get; set; }
/// <summary>
/// Gets or sets BCP 47 language identifier of tweet content
/// </summary>
[JsonPropertyName("lang")]
public string Language { get; set; }
}
}

Просмотреть файл

@ -1,30 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Collections.Generic;
using System.Text.Json;
namespace Microsoft.Toolkit.Services.Twitter
{
/// <summary>
/// Twitter Timeline Parser.
/// </summary>
public class TweetParser : Parsers.IParser<Tweet>
{
/// <summary>
/// Parse string data into strongly typed list.
/// </summary>
/// <param name="data">Input string.</param>
/// <returns>List of strongly typed objects.</returns>
public IEnumerable<Tweet> Parse(string data)
{
if (string.IsNullOrEmpty(data))
{
return null;
}
return JsonSerializer.Deserialize<List<Tweet>>(data);
}
}
}

Просмотреть файл

@ -1,28 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace Microsoft.Toolkit.Services.Twitter
{
/// <summary>
/// Longitude and Latitude for a tweet
/// </summary>
public class TwitterCoordinates
{
/// <summary>
/// Gets the numeric latitude (null if the value could not be converted)
/// </summary>
public double Latitude { get; internal set; }
/// <summary>
/// Gets the numeric longitude (null if the value could not be converted)
/// </summary>
public double Longitude { get; internal set; }
/// <inheritdoc/>
public override string ToString()
{
return $"({Latitude}, {Longitude})";
}
}
}

Просмотреть файл

@ -1,115 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Microsoft.Toolkit.Services.Twitter
{
internal class TwitterCoordinatesConverter : JsonConverter<TwitterCoordinates>
{
private readonly JsonEncodedText latitudeName = JsonEncodedText.Encode("Latitude");
private readonly JsonEncodedText longitudeName = JsonEncodedText.Encode("Longitude");
public override bool CanConvert(Type objectType)
{
return false;
}
private readonly JsonConverter<double> doubleConverter;
public TwitterCoordinatesConverter(JsonSerializerOptions options)
{
doubleConverter = options?.GetConverter(typeof(double)) as JsonConverter<double> ?? throw new InvalidOperationException();
}
public override TwitterCoordinates Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
try
{
if (reader.TokenType != JsonTokenType.StartObject)
{
return null;
}
double latitude = default;
bool latitudeSet = false;
double longitude = default;
bool longitudeSet = false;
// Get the first property.
reader.Read();
if (reader.TokenType != JsonTokenType.PropertyName)
{
throw new JsonException();
}
if (reader.ValueTextEquals(latitudeName.EncodedUtf8Bytes))
{
latitude = ReadProperty(ref reader, options);
latitudeSet = true;
}
else if (reader.ValueTextEquals(longitudeName.EncodedUtf8Bytes))
{
longitude = ReadProperty(ref reader, options);
longitudeSet = true;
}
else
{
throw new JsonException();
}
// Get the second property.
reader.Read();
if (reader.TokenType != JsonTokenType.PropertyName)
{
throw new JsonException();
}
if (latitudeSet && reader.ValueTextEquals(longitudeName.EncodedUtf8Bytes))
{
longitude = ReadProperty(ref reader, options);
}
else if (longitudeSet && reader.ValueTextEquals(latitudeName.EncodedUtf8Bytes))
{
latitude = ReadProperty(ref reader, options);
}
else
{
throw new JsonException();
}
reader.Read();
if (reader.TokenType != JsonTokenType.EndObject)
{
throw new JsonException();
}
return new TwitterCoordinates
{
Latitude = latitude,
Longitude = longitude
};
}
catch
{
return null;
}
}
private double ReadProperty(ref Utf8JsonReader reader, JsonSerializerOptions options)
{
reader.Read();
return doubleConverter.Read(ref reader, typeof(double), options);
}
public override void Write(Utf8JsonWriter writer, TwitterCoordinates value, JsonSerializerOptions options)
{
throw new NotImplementedException();
}
}
}

Просмотреть файл

@ -1,22 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace Microsoft.Toolkit.Services.Twitter
{
/// <summary>
/// Query string configuration.
/// </summary>
public class TwitterDataConfig
{
/// <summary>
/// Gets or sets twitter query type.
/// </summary>
public TwitterQueryType QueryType { get; set; }
/// <summary>
/// Gets or sets query parameters.
/// </summary>
public string Query { get; set; }
}
}

Просмотреть файл

@ -1,705 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.Toolkit.Services.Core;
#if WINRT
using System.Runtime.InteropServices.WindowsRuntime;
using Microsoft.Toolkit.Services.PlatformSpecific.Uwp;
using Windows.Storage.Streams;
#endif
#if NET462
using Microsoft.Toolkit.Services.PlatformSpecific.NetFramework;
#endif
namespace Microsoft.Toolkit.Services.Twitter
{
/// <summary>
/// Data Provider for connecting to Twitter service.
/// </summary>
public class TwitterDataProvider : Toolkit.Services.DataProviderBase<TwitterDataConfig, Toolkit.Parsers.SchemaBase>
{
/// <summary>
/// Base Url for service.
/// </summary>
private const string BaseUrl = "https://api.twitter.com/1.1";
private const string OAuthBaseUrl = "https://api.twitter.com/oauth";
private const string PublishUrl = "https://upload.twitter.com/1.1";
private const string UserStreamUrl = "https://userstream.twitter.com/1.1";
private static HttpClient _client;
/// <summary>
/// Base Url for service.
/// </summary>
private readonly TwitterOAuthTokens _tokens;
private readonly IAuthenticationBroker _authenticationBroker;
private readonly IPasswordManager _passwordManager;
private readonly IStorageManager _storageManager;
private readonly ISignatureManager _signatureManager;
private TwitterOAuthRequest _streamRequest;
/// <summary>
/// Gets or sets logged in user information.
/// </summary>
public string UserScreenName { get; set; }
/// <summary>
/// Gets a value indicating whether the provider is already logged in
/// </summary>
public bool LoggedIn { get; private set; }
/// <summary>
/// Initializes a new instance of the <see cref="TwitterDataProvider"/> class.
/// Constructor.
/// </summary>
/// <param name="tokens">OAuth tokens for request.</param>
/// <param name="authenticationBroker">Authentication result interface.</param>
/// <param name="passwordManager">Platform password manager</param>
/// <param name="storageManager">Platform storage provider</param>
/// <param name="signatureManager">Platform signature manager</param>
public TwitterDataProvider(TwitterOAuthTokens tokens, IAuthenticationBroker authenticationBroker, IPasswordManager passwordManager, IStorageManager storageManager, ISignatureManager signatureManager)
{
if (string.IsNullOrEmpty(tokens.ConsumerSecret))
{
throw new ArgumentException("Missing consumer secret");
}
if (string.IsNullOrEmpty(tokens.ConsumerKey))
{
throw new ArgumentException("Missing consumer key");
}
if (string.IsNullOrEmpty(tokens.CallbackUri))
{
throw new ArgumentException("Missing callback uri");
}
_tokens = tokens;
_authenticationBroker = authenticationBroker ?? throw new ArgumentException("Missing AuthenticationBroker");
_passwordManager = passwordManager ?? throw new ArgumentException("Missing PasswordManager");
_storageManager = storageManager ?? throw new ArgumentException("Missing StorageManager");
_signatureManager = signatureManager ?? throw new ArgumentException("Missing SignatureManager");
if (_client == null)
{
HttpClientHandler handler = new HttpClientHandler();
handler.AutomaticDecompression = DecompressionMethods.GZip;
_client = new HttpClient(handler);
}
}
#if WINRT
/// <summary>
/// Initializes a new instance of the <see cref="TwitterDataProvider"/> class.
/// Constructor.
/// </summary>
/// <param name="tokens">OAuth tokens for request.</param>
public TwitterDataProvider(TwitterOAuthTokens tokens)
: this(tokens, new UwpAuthenticationBroker(), new UwpPasswordManager(), new UwpStorageManager(), new UwpSignatureManager())
{
}
#endif
#if NET462
/// <summary>
/// Initializes a new instance of the <see cref="TwitterDataProvider"/> class.
/// Constructor.
/// </summary>
/// <param name="tokens">OAuth tokens for request.</param>
public TwitterDataProvider(TwitterOAuthTokens tokens)
: this(tokens, new NetFrameworkAuthenticationBroker(), new NetFrameworkPasswordManager(), new NetFrameworkStorageManager(), new NetFrameworkSignatureManager())
{
}
#endif
/// <summary>
/// Retrieve user data.
/// </summary>
/// <param name="screenName">User screen name or null for current logged user</param>
/// <returns>Returns user data.</returns>
public async Task<TwitterUser> GetUserAsync(string screenName = null)
{
string rawResult = null;
try
{
var userScreenName = screenName ?? UserScreenName;
var uri = new Uri($"{BaseUrl}/users/show.json?screen_name={userScreenName}");
TwitterOAuthRequest request = new TwitterOAuthRequest();
rawResult = await request.ExecuteGetAsync(uri, _tokens, _signatureManager);
return JsonSerializer.Deserialize<TwitterUser>(rawResult);
}
catch (UserNotFoundException)
{
throw new UserNotFoundException(screenName);
}
catch
{
if (!string.IsNullOrEmpty(rawResult))
{
var errors = JsonSerializer.Deserialize<TwitterErrors>(rawResult);
throw new TwitterException { Errors = errors };
}
throw;
}
}
/// <summary>
/// Retrieve user timeline data with specific parser.
/// </summary>
/// <typeparam name="TSchema">Strong type for results.</typeparam>
/// <param name="screenName">User screen name.</param>
/// <param name="maxRecords">Upper record limit.</param>
/// <param name="parser">Specific results parser.</param>
/// <returns>Returns strongly typed list of results.</returns>
public async Task<IEnumerable<TSchema>> GetUserTimeLineAsync<TSchema>(string screenName, int maxRecords, Toolkit.Parsers.IParser<TSchema> parser)
where TSchema : Toolkit.Parsers.SchemaBase
{
string rawResult = null;
try
{
var uri = new Uri($"{BaseUrl}/statuses/user_timeline.json?screen_name={screenName}&count={maxRecords}&include_rts=1&tweet_mode=extended");
TwitterOAuthRequest request = new TwitterOAuthRequest();
rawResult = await request.ExecuteGetAsync(uri, _tokens, _signatureManager);
var result = parser.Parse(rawResult);
return result
.Take(maxRecords)
.ToList();
}
catch (UserNotFoundException)
{
throw new UserNotFoundException(screenName);
}
catch
{
if (!string.IsNullOrEmpty(rawResult))
{
var errors = JsonSerializer.Deserialize<TwitterErrors>(rawResult);
throw new TwitterException { Errors = errors };
}
throw;
}
}
/// <summary>
/// Search for specific hash tag with specific parser.
/// </summary>
/// <typeparam name="TSchema">Strong type for results.</typeparam>
/// <param name="hashTag">Hash tag.</param>
/// <param name="maxRecords">Upper record limit.</param>
/// <param name="parser">Specific results parser.</param>
/// <returns>Returns strongly typed list of results.</returns>
public async Task<IEnumerable<TSchema>> SearchAsync<TSchema>(string hashTag, int maxRecords, Toolkit.Parsers.IParser<TSchema> parser)
where TSchema : Toolkit.Parsers.SchemaBase
{
var uri = new Uri($"{BaseUrl}/search/tweets.json?q={Uri.EscapeDataString(hashTag)}&count={maxRecords}&tweet_mode=extended");
TwitterOAuthRequest request = new TwitterOAuthRequest();
var rawResult = await request.ExecuteGetAsync(uri, _tokens, _signatureManager);
var result = parser.Parse(rawResult);
return result
.Take(maxRecords)
.ToList();
}
/// <summary>
/// Log user in to Twitter.
/// </summary>
/// <returns>Boolean indicating login success.</returns>
public async Task<bool> LoginAsync()
{
var credentials = _passwordManager.Get("TwitterAccessToken");
var user = await _storageManager.GetAsync("TwitterScreenName");
if (!string.IsNullOrEmpty(user) && credentials != null)
{
_tokens.AccessToken = credentials.UserName;
_tokens.AccessTokenSecret = credentials.Password;
UserScreenName = user;
LoggedIn = true;
return true;
}
if (await InitializeRequestAccessTokensAsync(_tokens.CallbackUri) == false)
{
LoggedIn = false;
return false;
}
string requestToken = _tokens.RequestToken;
string twitterUrl = $"{OAuthBaseUrl}/authorize?oauth_token={requestToken}";
Uri startUri = new Uri(twitterUrl);
Uri endUri = new Uri(_tokens.CallbackUri);
var result = await _authenticationBroker.Authenticate(startUri, endUri);
switch (result.ResponseStatus)
{
case AuthenticationResultStatus.Success:
LoggedIn = true;
return await ExchangeRequestTokenForAccessTokenAsync(result.ResponseData);
case AuthenticationResultStatus.ErrorHttp:
Debug.WriteLine("WAB failed, message={0}", result.ResponseErrorDetail.ToString());
LoggedIn = false;
return false;
case AuthenticationResultStatus.UserCancel:
Debug.WriteLine("WAB user aborted.");
LoggedIn = false;
return false;
}
LoggedIn = false;
return false;
}
/// <summary>
/// Log user out of Twitter.
/// </summary>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public async Task LogoutAsync()
{
var credential = _passwordManager.Get("TwitterAccessToken");
if (credential != null)
{
_passwordManager.Remove("TwitterAccessToken");
await _storageManager.SetAsync("TwitterScreenName", null);
}
UserScreenName = null;
LoggedIn = false;
}
/// <summary>
/// Tweets a status update.
/// </summary>
/// <param name="tweet">Tweet text.</param>
/// <param name="pictures">Pictures to attach to the tweet (up to 4).</param>
/// <returns>Success or failure.</returns>
public async Task<bool> TweetStatusAsync(string tweet, params Stream[] pictures)
{
return await TweetStatusAsync(new TwitterStatus { Message = tweet }, pictures);
}
/// <summary>
/// Tweets a status update.
/// </summary>
/// <param name="status">Tweet text.</param>
/// <param name="pictures">Pictures to attach to the tweet (up to 4).</param>
/// <returns>Success or failure.</returns>
public async Task<bool> TweetStatusAsync(TwitterStatus status, params Stream[] pictures)
{
var mediaIds = string.Empty;
if (pictures != null && pictures.Length > 0)
{
var ids = new List<string>();
foreach (var picture in pictures)
{
ids.Add(await UploadPictureAsync(picture));
}
mediaIds = "&media_ids=" + string.Join(",", ids);
}
var uri = new Uri($"{BaseUrl}/statuses/update.json?{status.RequestParameters}{mediaIds}");
TwitterOAuthRequest request = new TwitterOAuthRequest();
await request.ExecutePostAsync(uri, _tokens, _signatureManager);
return true;
}
/// <summary>
/// Publish a picture to Twitter user's medias.
/// </summary>
/// <param name="stream">Picture stream.</param>
/// <returns>Media ID</returns>
public async Task<string> UploadPictureAsync(Stream stream)
{
var uri = new Uri($"{PublishUrl}/media/upload.json");
byte[] fileBytes;
using (var ms = new MemoryStream())
{
await stream.CopyToAsync(ms);
fileBytes = ms.ToArray();
}
string boundary = DateTime.Now.Ticks.ToString("x");
TwitterOAuthRequest request = new TwitterOAuthRequest();
return await request.ExecutePostMultipartAsync(uri, _tokens, boundary, fileBytes, _signatureManager);
}
/// <summary>
/// Open a connection to user streams service (Events, DirectMessages...).
/// </summary>
/// <param name="parser">Specific stream's result parser.</param>
/// <param name="callback">Method invoked each time a result occurs.</param>
/// <returns>Awaitable task.</returns>
public Task StartUserStreamAsync(TwitterUserStreamParser parser, TwitterStreamCallbacks.TwitterStreamCallback callback)
{
var uri = new Uri($"{UserStreamUrl}/user.json?replies=all");
_streamRequest = new TwitterOAuthRequest();
return _streamRequest.ExecuteGetStreamAsync(uri, _tokens, rawResult => callback(parser.Parse(rawResult)), _signatureManager);
}
/// <summary>
/// Stop user's stream
/// </summary>
public void StopStream()
{
_streamRequest?.Abort();
_streamRequest = null;
}
/// <summary>
/// Returns parser implementation for specified configuration.
/// </summary>
/// <param name="config">Query configuration.</param>
/// <returns>Strongly typed parser.</returns>
protected override Toolkit.Parsers.IParser<Toolkit.Parsers.SchemaBase> GetDefaultParser(TwitterDataConfig config)
{
if (config == null)
{
throw new ConfigNullException();
}
switch (config.QueryType)
{
case TwitterQueryType.Search:
return new TwitterSearchParser();
case TwitterQueryType.Home:
case TwitterQueryType.User:
case TwitterQueryType.Custom:
return new TwitterParser<Toolkit.Parsers.SchemaBase>();
default:
return new TwitterParser<Toolkit.Parsers.SchemaBase>();
}
}
/// <summary>
/// Wrapper around REST API for making data request.
/// </summary>
/// <typeparam name="TSchema">Schema to use</typeparam>
/// <param name="config">Query configuration.</param>
/// <param name="maxRecords">Upper limit for records returned.</param>
/// <param name="pageIndex">The zero-based index of the page that corresponds to the items to retrieve.</param>
/// <param name="parser">IParser implementation for interpreting results.</param>
/// <returns>Strongly typed list of results.</returns>
protected override async Task<IEnumerable<TSchema>> GetDataAsync<TSchema>(TwitterDataConfig config, int maxRecords, int pageIndex, Toolkit.Parsers.IParser<TSchema> parser)
{
IEnumerable<TSchema> items;
switch (config.QueryType)
{
case TwitterQueryType.User:
items = await GetUserTimeLineAsync(config.Query, maxRecords, parser);
break;
case TwitterQueryType.Search:
items = await SearchAsync(config.Query, maxRecords, parser);
break;
case TwitterQueryType.Custom:
items = await GetCustomSearch(config.Query, parser);
break;
case TwitterQueryType.Home:
default:
items = await GetHomeTimeLineAsync(maxRecords, parser);
break;
}
return items;
}
/// <summary>
/// Check validity of configuration.
/// </summary>
/// <param name="config">Query configuration.</param>
protected override void ValidateConfig(TwitterDataConfig config)
{
if (config?.Query == null && config?.QueryType != TwitterQueryType.Home)
{
throw new ConfigParameterNullException(nameof(config.Query));
}
if (_tokens == null)
{
throw new ConfigParameterNullException(nameof(_tokens));
}
if (string.IsNullOrEmpty(_tokens.ConsumerKey))
{
throw new OAuthKeysNotPresentException(nameof(_tokens.ConsumerKey));
}
if (string.IsNullOrEmpty(_tokens.ConsumerSecret))
{
throw new OAuthKeysNotPresentException(nameof(_tokens.ConsumerSecret));
}
}
/// <summary>
/// Extract requested token from the REST API response string.
/// </summary>
/// <param name="getResponse">REST API response string.</param>
/// <param name="tokenType">Token type to retrieve.</param>
/// <returns>Required token.</returns>
private static string ExtractTokenFromResponse(string getResponse, TwitterOAuthTokenType tokenType)
{
string requestOrAccessToken = null;
string requestOrAccessTokenSecret = null;
string oauthVerifier = null;
string oauthCallbackConfirmed = null;
string screenName = null;
string[] keyValPairs = getResponse.Split('&');
for (int i = 0; i < keyValPairs.Length; i++)
{
string[] splits = keyValPairs[i].Split('=');
switch (splits[0])
{
case "screen_name":
screenName = splits[1];
break;
case "oauth_token":
requestOrAccessToken = splits[1];
break;
case "oauth_token_secret":
requestOrAccessTokenSecret = splits[1];
break;
case "oauth_callback_confirmed":
oauthCallbackConfirmed = splits[1];
break;
case "oauth_verifier":
oauthVerifier = splits[1];
break;
}
}
switch (tokenType)
{
case TwitterOAuthTokenType.OAuthRequestOrAccessToken:
return requestOrAccessToken;
case TwitterOAuthTokenType.OAuthRequestOrAccessTokenSecret:
return requestOrAccessTokenSecret;
case TwitterOAuthTokenType.OAuthVerifier:
return oauthVerifier;
case TwitterOAuthTokenType.ScreenName:
return screenName;
case TwitterOAuthTokenType.OAuthCallbackConfirmed:
return oauthCallbackConfirmed;
}
return string.Empty;
}
/// <summary>
/// Get home time line data.
/// </summary>
/// <typeparam name="TSchema">Strong typed result.</typeparam>
/// <param name="maxRecords">Upper record limit.</param>
/// <param name="parser">Specific result parser.</param>
/// <returns>Return strong typed list of results.</returns>
private async Task<IEnumerable<TSchema>> GetHomeTimeLineAsync<TSchema>(int maxRecords, Toolkit.Parsers.IParser<TSchema> parser)
where TSchema : Toolkit.Parsers.SchemaBase
{
var uri = new Uri($"{BaseUrl}/statuses/home_timeline.json?count={maxRecords}&tweet_mode=extended");
TwitterOAuthRequest request = new TwitterOAuthRequest();
var rawResult = await request.ExecuteGetAsync(uri, _tokens, _signatureManager);
return parser.Parse(rawResult);
}
private async Task<IEnumerable<TSchema>> GetCustomSearch<TSchema>(string query, Toolkit.Parsers.IParser<TSchema> parser)
where TSchema : Toolkit.Parsers.SchemaBase
{
var uri = new Uri($"{BaseUrl}/{query}");
TwitterOAuthRequest request = new TwitterOAuthRequest();
var rawResult = await request.ExecuteGetAsync(uri, _tokens, _signatureManager);
return parser.Parse(rawResult);
}
/// <summary>
/// Package up token request.
/// </summary>
/// <param name="twitterCallbackUrl">Callback Uri.</param>
/// <returns>Success or failure.</returns>
private async Task<bool> InitializeRequestAccessTokensAsync(string twitterCallbackUrl)
{
var twitterUrl = $"{OAuthBaseUrl}/request_token";
string nonce = GetNonce();
string timeStamp = GetTimeStamp();
string sigBaseStringParams = GetSignatureBaseStringParams(_tokens.ConsumerKey, nonce, timeStamp, "oauth_callback=" + Uri.EscapeDataString(twitterCallbackUrl));
string sigBaseString = "GET&" + Uri.EscapeDataString(twitterUrl) + "&" + Uri.EscapeDataString(sigBaseStringParams);
string signature = _signatureManager.GetSignature(sigBaseString, _tokens.ConsumerSecret, true);
twitterUrl += "?" + sigBaseStringParams + "&oauth_signature=" + Uri.EscapeDataString(signature);
string getResponse;
using (var request = new HttpRequestMessage(HttpMethod.Get, new Uri(twitterUrl)))
{
using var response = await _client.SendAsync(request).ConfigureAwait(false);
var data = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
if (response.IsSuccessStatusCode)
{
getResponse = data;
}
else
{
Debug.WriteLine("HttpHelper call failed trying to retrieve Twitter Request Tokens. Message: {0}", data);
return false;
}
}
var callbackConfirmed = ExtractTokenFromResponse(getResponse, TwitterOAuthTokenType.OAuthCallbackConfirmed);
if (Convert.ToBoolean(callbackConfirmed) != true)
{
return false;
}
_tokens.RequestToken = ExtractTokenFromResponse(getResponse, TwitterOAuthTokenType.OAuthRequestOrAccessToken);
_tokens.RequestTokenSecret = ExtractTokenFromResponse(getResponse, TwitterOAuthTokenType.OAuthRequestOrAccessTokenSecret);
return true;
}
/// <summary>
/// Build signature base string.
/// </summary>
/// <param name="consumerKey">Consumer Key.</param>
/// <param name="nonce">Nonce.</param>
/// <param name="timeStamp">Timestamp.</param>
/// <param name="additionalParameters">Any additional parameter name/values that need appending to base string.</param>
/// <returns>Signature base string.</returns>
private string GetSignatureBaseStringParams(string consumerKey, string nonce, string timeStamp, string additionalParameters = "")
{
string sigBaseStringParams = additionalParameters;
sigBaseStringParams += "&" + "oauth_consumer_key=" + consumerKey;
sigBaseStringParams += "&" + "oauth_nonce=" + nonce;
sigBaseStringParams += "&" + "oauth_signature_method=HMAC-SHA1";
sigBaseStringParams += "&" + "oauth_timestamp=" + timeStamp;
sigBaseStringParams += "&" + "oauth_version=1.0";
return sigBaseStringParams;
}
/// <summary>
/// Extract and initialize access tokens.
/// </summary>
/// <param name="webAuthResultResponseData">WAB data containing appropriate tokens.</param>
/// <returns>Success or failure.</returns>
private async Task<bool> ExchangeRequestTokenForAccessTokenAsync(string webAuthResultResponseData)
{
string responseData = webAuthResultResponseData.Substring(webAuthResultResponseData.IndexOf("oauth_token"));
string requestToken = ExtractTokenFromResponse(responseData, TwitterOAuthTokenType.OAuthRequestOrAccessToken);
// Ensure requestToken matches accessToken per Twitter documentation.
if (requestToken != _tokens.RequestToken)
{
return false;
}
string oAuthVerifier = ExtractTokenFromResponse(responseData, TwitterOAuthTokenType.OAuthVerifier);
string twitterUrl = $"{OAuthBaseUrl}/access_token";
string timeStamp = GetTimeStamp();
string nonce = GetNonce();
string sigBaseStringParams = GetSignatureBaseStringParams(_tokens.ConsumerKey, nonce, timeStamp, "oauth_token=" + requestToken);
string sigBaseString = "POST&";
sigBaseString += Uri.EscapeDataString(twitterUrl) + "&" + Uri.EscapeDataString(sigBaseStringParams);
string signature = _signatureManager.GetSignature(sigBaseString, _tokens.ConsumerSecret);
string data = null;
string authorizationHeaderParams = "oauth_consumer_key=\"" + _tokens.ConsumerKey + "\", oauth_nonce=\"" + nonce + "\", oauth_signature_method=\"HMAC-SHA1\", oauth_signature=\"" + Uri.EscapeDataString(signature) + "\", oauth_timestamp=\"" + timeStamp + "\", oauth_token=\"" + Uri.EscapeDataString(requestToken) + "\", oauth_verifier=\"" + Uri.EscapeUriString(oAuthVerifier) + "\" , oauth_version=\"1.0\"";
using (var request = new HttpRequestMessage(HttpMethod.Post, new Uri(twitterUrl)))
{
request.Headers.Authorization = new AuthenticationHeaderValue("OAuth", authorizationHeaderParams);
using var response = await _client.SendAsync(request).ConfigureAwait(false);
data = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
}
var screenName = ExtractTokenFromResponse(data, TwitterOAuthTokenType.ScreenName);
var accessToken = ExtractTokenFromResponse(data, TwitterOAuthTokenType.OAuthRequestOrAccessToken);
var accessTokenSecret = ExtractTokenFromResponse(data, TwitterOAuthTokenType.OAuthRequestOrAccessTokenSecret);
UserScreenName = screenName;
_tokens.AccessToken = accessToken;
_tokens.AccessTokenSecret = accessTokenSecret;
_passwordManager.Store("TwitterAccessToken", new PasswordCredential { UserName = accessToken, Password = accessTokenSecret });
await _storageManager.SetAsync("TwitterScreenName", screenName);
return true;
}
/// <summary>
/// Generate nonce.
/// </summary>
/// <returns>Nonce.</returns>
private string GetNonce()
{
Random rand = new Random();
int nonce = rand.Next(1000000000);
return nonce.ToString();
}
/// <summary>
/// Generate timestamp.
/// </summary>
/// <returns>Timestamp.</returns>
private string GetTimeStamp()
{
TimeSpan sinceEpoch = DateTime.UtcNow - new DateTime(1970, 1, 1);
return Math.Round(sinceEpoch.TotalSeconds).ToString();
}
}
}

Просмотреть файл

@ -1,103 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Globalization;
using System.Text.Json.Serialization;
namespace Microsoft.Toolkit.Services.Twitter
{
/// <summary>
/// Twitter User type.
/// </summary>
public class TwitterDirectMessage : ITwitterResult
{
/// <summary>
/// Gets or sets the direct message id.
/// </summary>
/// <value>The direct message id.</value>
[JsonPropertyName("id")]
public decimal Id { get; set; }
/// <summary>
/// Gets or sets the sender id.
/// </summary>
/// <value>The sender id.</value>
[JsonPropertyName("sender_id")]
public decimal SenderId { get; set; }
/// <summary>
/// Gets or sets the direct message text.
/// </summary>
/// <value>The direct message text.</value>
[JsonPropertyName("text")]
public string Text { get; set; }
/// <summary>
/// Gets or sets the recipient id.
/// </summary>
/// <value>The recipient id.</value>
[JsonPropertyName("recipient_id")]
public decimal RecipientId { get; set; }
/// <summary>
/// Gets or sets the created date.
/// </summary>
/// <value>The created date.</value>
[JsonPropertyName("created_at")]
public string CreatedAt { get; set; }
/// <summary>
/// Gets or sets the name of the sender screen.
/// </summary>
/// <value>The name of the sender screen.</value>
[JsonPropertyName("sender_screen_name")]
public string SenderScreenName { get; set; }
/// <summary>
/// Gets or sets the name of the recipient screen.
/// </summary>
/// <value>The name of the recipient screen.</value>
[JsonPropertyName("recipient_screen_name")]
public string RecipientScreenName { get; set; }
/// <summary>
/// Gets or sets the sender.
/// </summary>
/// <value>The sender.</value>
[JsonPropertyName("sender")]
public TwitterUser Sender { get; set; }
/// <summary>
/// Gets or sets the recipient.
/// </summary>
/// <value>The recipient.</value>
[JsonPropertyName("recipient")]
public TwitterUser Recipient { get; set; }
/// <summary>
/// Gets or sets the entities.
/// </summary>
/// <value>The entities.</value>
[JsonPropertyName("entities")]
public TwitterEntities Entities { get; set; }
/// <summary>
/// Gets the creation date
/// </summary>
public DateTime CreationDate
{
get
{
DateTime dt;
if (!DateTime.TryParseExact(CreatedAt, "ddd MMM dd HH:mm:ss zzzz yyyy", CultureInfo.InvariantCulture, DateTimeStyles.None, out dt))
{
dt = DateTime.Today;
}
return dt;
}
}
}
}

Просмотреть файл

@ -1,57 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Text.Json.Serialization;
namespace Microsoft.Toolkit.Services.Twitter
{
/// <summary>
/// Twitter Entities containing Twitter entities object tweet
/// </summary>
public class TwitterEntities
{
/// <summary>
/// Gets or sets Hashtags array of the tweet.
/// This array will be empty if no Hashtags are present.
/// </summary>
[JsonPropertyName("Hashtags")]
public TwitterHashtag[] Hashtags { get; set; }
/// <summary>
/// Gets or sets Symbols array of the tweet.
/// This array will be empty if no Symbols are present.
/// </summary>
[JsonPropertyName("Symbols")]
public TwitterSymbol[] Symbols { get; set; }
/// <summary>
/// Gets or sets Media array of the tweet.
/// This array will not exist if no media is present.
/// </summary>
[JsonPropertyName("media")]
public TwitterMedia[] Media { get; set; }
/// <summary>
/// Gets or sets Urls array of the tweet.
/// This array will be empty if no Urls are present.
/// </summary>
[JsonPropertyName("urls")]
public TwitterUrl[] Urls { get; set; }
/// <summary>
/// Gets or sets array of usernames mentioned in the tweet.
/// This array will be empty if no usernames are mentioned.
/// </summary>
[JsonPropertyName("user_mentions")]
public TwitterUserMention[] UserMentions { get; set; }
/// <summary>
/// Gets or sets the poll in a tweet.
/// This array will not exist if no poll is present.
/// This array will always have one poll.
/// </summary>
[JsonPropertyName("polls")]
public TwitterPoll Poll { get; set; }
}
}

Просмотреть файл

@ -1,26 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Text.Json.Serialization;
namespace Microsoft.Toolkit.Services.Twitter
{
/// <summary>
/// Twitter error type
/// </summary>
public class TwitterError
{
/// <summary>
/// Gets or sets error code
/// </summary>
[JsonPropertyName("code")]
public int Code { get; set; }
/// <summary>
/// Gets or sets error message
/// </summary>
[JsonPropertyName("message")]
public string Message { get; set; }
}
}

Просмотреть файл

@ -1,20 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Text.Json.Serialization;
namespace Microsoft.Toolkit.Services.Twitter
{
/// <summary>
/// Twitter errors type.
/// </summary>
public class TwitterErrors
{
/// <summary>
/// Gets or sets the list of errors
/// </summary>
[JsonPropertyName("errors")]
public TwitterError[] Errors { get; set; }
}
}

Просмотреть файл

@ -1,19 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
namespace Microsoft.Toolkit.Services.Twitter
{
/// <summary>
/// Twitter specific exception.
/// </summary>
public class TwitterException : Exception
{
/// <summary>
/// Gets or sets the errors returned by Twitter
/// </summary>
public TwitterErrors Errors { get; set; }
}
}

Просмотреть файл

@ -1,20 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Text.Json.Serialization;
namespace Microsoft.Toolkit.Services.Twitter
{
/// <summary>
/// Twitter User type.
/// </summary>
public class TwitterExtended
{
/// <summary>
/// Gets or sets the text of the tweet (280 characters).
/// </summary>
[JsonPropertyName("full_text")]
public string FullText { get; set; }
}
}

Просмотреть файл

@ -1,20 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Text.Json.Serialization;
namespace Microsoft.Toolkit.Services.Twitter
{
/// <summary>
/// Extended Entities containing an array of TwitterMedia.
/// </summary>
public class TwitterExtendedEntities
{
/// <summary>
/// Gets or sets Media of the tweet.
/// </summary>
[JsonPropertyName("media")]
public TwitterMedia[] Media { get; set; }
}
}

Просмотреть файл

@ -1,87 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Globalization;
using System.Text.Json.Serialization;
namespace Microsoft.Toolkit.Services.Twitter
{
/// <summary>
/// A class to contain the latitude and longitude of a tweet.
/// </summary>
public class TwitterGeoData
{
private const int LatitudeIndex = 0;
private const int LongitudeIndex = 1;
private const string PointType = "Point";
/// <summary>
/// Gets or sets the type of data
/// </summary>
[JsonPropertyName("type")]
public string DataType { get; set; }
/// <summary>
/// Gets the latitude and longitude in a coordinate format.
/// </summary>
public string DisplayCoordinates
{
get
{
string result = null;
if (Coordinates != null)
{
result = $"({Coordinates[LatitudeIndex]}, {Coordinates[LongitudeIndex]})";
}
return result;
}
}
/// <summary>
/// Gets or sets the coordinates of the geographic data
/// </summary>
[JsonPropertyName("coordinates")]
public string[] Coordinates { get; set; }
/// <summary>
/// Gets the numeric latitude (null if the value could not be converted)
/// </summary>
public double? Latitude
{
get
{
return ParseCoordinate(LatitudeIndex);
}
}
/// <summary>
/// Gets the numeric longitude (null if the value could not be converted)
/// </summary>
public double? Longitude
{
get
{
return ParseCoordinate(LongitudeIndex);
}
}
private double? ParseCoordinate(int index)
{
double? result = null;
double parsed;
if (DataType == PointType
&& Coordinates != null
&& !string.IsNullOrEmpty(Coordinates[index])
&& double.TryParse(Coordinates[index], NumberStyles.AllowDecimalPoint | NumberStyles.AllowLeadingSign, CultureInfo.InvariantCulture, out parsed))
{
result = parsed;
}
return result;
}
}
}

Просмотреть файл

@ -1,26 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Text.Json.Serialization;
namespace Microsoft.Toolkit.Services.Twitter
{
/// <summary>
/// Twitter Hashtag object containing all hashtags in a tweet.
/// </summary>
public class TwitterHashtag
{
/// <summary>
/// Gets or sets indices of hashtag location in tweet string.
/// </summary>
[JsonPropertyName("indices")]
public int[] Indices { get; set; }
/// <summary>
/// Gets or sets hashtag text, excluding #.
/// </summary>
[JsonPropertyName("text")]
public string Text { get; set; }
}
}

Просмотреть файл

@ -1,86 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Text.Json.Serialization;
namespace Microsoft.Toolkit.Services.Twitter
{
/// <summary>
/// Twitter Media
/// </summary>
public class TwitterMedia
{
/// <summary>
/// Gets or sets ID as string.
/// </summary>
[JsonPropertyName("id_str")]
public string Id { get; set; }
/// <summary>
/// Gets or sets indices array.
/// </summary>
[JsonPropertyName("indices")]
public int[] Indices { get; set; }
/// <summary>
/// Gets or sets MediaUrl (direct link to image).
/// </summary>
[JsonPropertyName("media_url")]
public string MediaUrl { get; set; }
/// <summary>
/// Gets or sets HTTPS MediaUrl.
/// </summary>
[JsonPropertyName("media_url_https")]
public string MediaUrlHttps { get; set; }
/// <summary>
/// Gets or sets t.co shortened tweet Url.
/// </summary>
[JsonPropertyName("url")]
public string Url { get; set; }
/// <summary>
/// Gets or sets DisplayUrl (pics.twitter.com Url).
/// </summary>
[JsonPropertyName("display_url")]
public string DisplayUrl { get; set; }
/// <summary>
/// Gets or sets DisplayUrl (pics.twitter.com Url).
/// </summary>
[JsonPropertyName("expanded_url")]
public string ExpandedUrl { get; set; }
/// <summary>
/// Gets or sets MediaType - photo, animated_gif, or video
/// </summary>
[JsonPropertyName("type")]
public string MediaType { get; set; }
/// <summary>
/// Gets or sets size array
/// </summary>
[JsonPropertyName("sizes")]
public TwitterMediaSizes Sizes { get; set; }
/// <summary>
/// Gets or sets the SourceId - tweet ID of media's original tweet
/// </summary>
[JsonPropertyName("source_status_id_str")]
public string SourceIdStr { get; set; }
/// <summary>
/// Gets or sets metadata for video attached to tweet
/// </summary>
[JsonPropertyName("video_info")]
public TwitterMediaVideoInfo VideoInfo { get; set; }
/// <summary>
/// Gets or sets extended metadata for video attached to tweet.
/// </summary>
[JsonPropertyName("additional_media_info")]
public TwitterMediaAdditionalInfo AdditionalMediaInfo { get; set; }
}
}

Просмотреть файл

@ -1,38 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Text.Json.Serialization;
namespace Microsoft.Toolkit.Services.Twitter
{
/// <summary>
/// Twitter Media Info
/// </summary>
public class TwitterMediaAdditionalInfo
{
/// <summary>
/// Gets or sets title of video
/// </summary>
[JsonPropertyName("title")]
public string Title { get; set; }
/// <summary>
/// Gets or sets description of video
/// </summary>
[JsonPropertyName("description")]
public string Description { get; set; }
/// <summary>
/// Gets or sets a value indicating whether video is embeddable
/// </summary>
[JsonPropertyName("embeddable")]
public bool Embeddable { get; set; }
/// <summary>
/// Gets or sets a value indicating whether "monetizable"
/// </summary>
[JsonPropertyName("monetizable")]
public bool Monetizable { get; set; }
}
}

Просмотреть файл

@ -1,32 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Text.Json.Serialization;
namespace Microsoft.Toolkit.Services.Twitter
{
/// <summary>
/// Twitter Entities containing size details for each size of an image.
/// </summary>
public class TwitterMediaSizeData
{
/// <summary>
/// Gets or sets width integer.
/// </summary>
[JsonPropertyName("w")]
public int Width { get; set; }
/// <summary>
/// Gets or sets height integer.
/// </summary>
[JsonPropertyName("h")]
public int Height { get; set; }
/// <summary>
/// Gets or sets resize string.
/// </summary>
[JsonPropertyName("resize")]
public string Resize { get; set; }
}
}

Просмотреть файл

@ -1,38 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Text.Json.Serialization;
namespace Microsoft.Toolkit.Services.Twitter
{
/// <summary>
/// Twitter Media Sizes containing size data for different image sizes.
/// </summary>
public class TwitterMediaSizes
{
/// <summary>
/// Gets or sets small metadata.
/// </summary>
[JsonPropertyName("small")]
public TwitterMediaSizeData Small { get; set; }
/// <summary>
/// Gets or sets thumbnail metadata.
/// </summary>
[JsonPropertyName("thumb")]
public TwitterMediaSizeData Thumb { get; set; }
/// <summary>
/// Gets or sets large metadata.
/// </summary>
[JsonPropertyName("large")]
public TwitterMediaSizeData Large { get; set; }
/// <summary>
/// Gets or sets medium metadata.
/// </summary>
[JsonPropertyName("medium")]
public TwitterMediaSizeData Medium { get; set; }
}
}

Просмотреть файл

@ -1,32 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Text.Json.Serialization;
namespace Microsoft.Toolkit.Services.Twitter
{
/// <summary>
/// Twitter Video information
/// </summary>
public class TwitterMediaVideoInfo
{
/// <summary>
/// Gets or sets video aspect ratio (width, height)
/// </summary>
[JsonPropertyName("aspect_ratio")]
public int[] AspectRatio { get; set; }
/// <summary>
/// Gets or sets duration of video in milliseconds
/// </summary>
[JsonPropertyName("duration_millis")]
public int Duration { get; set; }
/// <summary>
/// Gets or sets video variants for different codecs, bitrates, etc.
/// </summary>
[JsonPropertyName("variants")]
public TwitterMediaVideoVariants[] Variants { get; set; }
}
}

Просмотреть файл

@ -1,32 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Text.Json.Serialization;
namespace Microsoft.Toolkit.Services.Twitter
{
/// <summary>
/// Twitter Video properties
/// </summary>
public class TwitterMediaVideoVariants
{
/// <summary>
/// Gets or sets video bitrate in bits-per-second
/// </summary>
[JsonPropertyName("bitrate")]
public int Bitrate { get; set; }
/// <summary>
/// Gets or sets the MIME type of the video
/// </summary>
[JsonPropertyName("content_type")]
public string ContentType { get; set; }
/// <summary>
/// Gets or sets the direct URL for the video variant
/// </summary>
[JsonPropertyName("url")]
public string Url { get; set; }
}
}

Просмотреть файл

@ -1,168 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.IO;
using System.IO.Compression;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.Toolkit.Services.Core;
namespace Microsoft.Toolkit.Services.Twitter
{
/// <summary>
/// OAuth request.
/// </summary>
internal class TwitterOAuthRequest
{
private static HttpClient client;
private bool _abort;
/// <summary>
/// Initializes a new instance of the <see cref="TwitterOAuthRequest"/> class.
/// </summary>
public TwitterOAuthRequest()
{
if (client == null)
{
HttpClientHandler handler = new HttpClientHandler();
handler.AutomaticDecompression = DecompressionMethods.GZip;
client = new HttpClient(handler);
}
}
/// <summary>
/// HTTP Get request to specified Uri.
/// </summary>
/// <param name="requestUri">Uri to make OAuth request.</param>
/// <param name="tokens">Tokens to pass in request.</param>
/// <param name="signatureManager">Signature manager to sign the OAuth request</param>
/// <returns>String result.</returns>
public async Task<string> ExecuteGetAsync(Uri requestUri, TwitterOAuthTokens tokens, ISignatureManager signatureManager)
{
using var request = new HttpRequestMessage(HttpMethod.Get, requestUri);
var requestBuilder = new TwitterOAuthRequestBuilder(requestUri, tokens, signatureManager, "GET");
request.Headers.Authorization = AuthenticationHeaderValue.Parse(requestBuilder.AuthorizationHeader);
using var response = await client.SendAsync(request).ConfigureAwait(false);
response.ThrowIfNotValid();
return ProcessErrors(await response.Content.ReadAsStringAsync().ConfigureAwait(false));
}
/// <summary>
/// HTTP Get request for stream service.
/// </summary>
/// <param name="requestUri">Uri to make OAuth request.</param>
/// <param name="tokens">Tokens to pass in request.</param>
/// <param name="callback">Function invoked when stream available.</param>
/// <param name="signatureManager">Signature manager to sign the OAuth requests</param>
/// <returns>awaitable task</returns>
public async Task ExecuteGetStreamAsync(Uri requestUri, TwitterOAuthTokens tokens, TwitterStreamCallbacks.RawJsonCallback callback, ISignatureManager signatureManager)
{
using var request = new HttpRequestMessage(HttpMethod.Get, requestUri);
var requestBuilder = new TwitterOAuthRequestBuilder(requestUri, tokens, signatureManager);
request.Headers.Authorization = AuthenticationHeaderValue.Parse(requestBuilder.AuthorizationHeader);
using var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false);
response.ThrowIfNotValid();
using var responseStream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
using var reader = new StreamReader(responseStream);
while (!_abort && !reader.EndOfStream)
{
var result = reader.ReadLine();
if (!string.IsNullOrEmpty(result))
{
callback?.Invoke(result);
}
}
}
/// <summary>
/// Stop reading stream
/// </summary>
public void Abort()
{
_abort = true;
}
/// <summary>
/// HTTP Post request to specified Uri.
/// </summary>
/// <param name="requestUri">Uri to make OAuth request.</param>
/// <param name="tokens">Tokens to pass in request.</param>
/// <param name="signatureManager">Signature manager to sign the OAuth requests</param>
/// <returns>String result.</returns>
public async Task<string> ExecutePostAsync(Uri requestUri, TwitterOAuthTokens tokens, ISignatureManager signatureManager)
{
using var request = new HttpRequestMessage(HttpMethod.Post, requestUri);
var requestBuilder = new TwitterOAuthRequestBuilder(requestUri, tokens, signatureManager, "POST");
request.Headers.Authorization = AuthenticationHeaderValue.Parse(requestBuilder.AuthorizationHeader);
using var response = await client.SendAsync(request).ConfigureAwait(false);
response.ThrowIfNotValid();
return ProcessErrors(await response.Content.ReadAsStringAsync().ConfigureAwait(false));
}
/// <summary>
/// HTTP Post request to specified Uri.
/// </summary>
/// <param name="requestUri">Uri to make OAuth request.</param>
/// <param name="tokens">Tokens to pass in request.</param>
/// <param name="boundary">Boundary used to separate data.</param>
/// <param name="content">Data to post to server.</param>
/// <param name="signatureManager">Signature manager to sign the OAuth requests</param>
/// <returns>String result.</returns>
public async Task<string> ExecutePostMultipartAsync(Uri requestUri, TwitterOAuthTokens tokens, string boundary, byte[] content, ISignatureManager signatureManager)
{
JsonElement mediaId = default;
try
{
using var multipartFormDataContent = new MultipartFormDataContent(boundary);
using var byteContent = new ByteArrayContent(content);
multipartFormDataContent.Add(byteContent, "media");
using var request = new HttpRequestMessage(HttpMethod.Post, requestUri);
var requestBuilder = new TwitterOAuthRequestBuilder(requestUri, tokens, signatureManager, "POST");
request.Headers.Authorization = AuthenticationHeaderValue.Parse(requestBuilder.AuthorizationHeader);
request.Content = multipartFormDataContent;
using var response = await client.SendAsync(request).ConfigureAwait(false);
response.ThrowIfNotValid();
using var jsonResult = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
var jObj = await JsonDocument.ParseAsync(jsonResult).ConfigureAwait(false);
mediaId = jObj.RootElement.GetProperty("media_id_string");
}
catch (ObjectDisposedException)
{
// known issue
// http://stackoverflow.com/questions/39109060/httpmultipartformdatacontent-dispose-throws-objectdisposedexception
}
return mediaId.ToString();
}
private string ProcessErrors(string content)
{
if (content.StartsWith("{\"errors\":"))
{
var errors = JsonSerializer.Deserialize<TwitterErrors>(content);
throw new TwitterException { Errors = errors };
}
return content;
}
}
}

Просмотреть файл

@ -1,251 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using Microsoft.Toolkit.Services.Core;
using Microsoft.Toolkit.Services.OAuth;
namespace Microsoft.Toolkit.Services.Twitter
{
/// <summary>
/// OAuth request builder.
/// </summary>
internal class TwitterOAuthRequestBuilder
{
private ISignatureManager _signatureManager;
/// <summary>
/// Realm for request.
/// </summary>
public const string Realm = "Twitter API";
/// <summary>
/// Gets or sets HTTP verb for request.
/// </summary>
public string Verb { get; set; }
/// <summary>
/// Gets encoded Request Uri.
/// </summary>
public Uri EncodedRequestUri { get; private set; }
/// <summary>
/// Gets request Uri without query.
/// </summary>
public Uri RequestUriWithoutQuery { get; private set; }
/// <summary>
/// Gets list of query parameters.
/// </summary>
public IEnumerable<OAuthParameter> QueryParams { get; private set; }
/// <summary>
/// Gets version.
/// </summary>
public OAuthParameter Version { get; private set; }
/// <summary>
/// Gets nonce.
/// </summary>
public OAuthParameter Nonce { get; private set; }
/// <summary>
/// Gets timestamp.
/// </summary>
public OAuthParameter Timestamp { get; private set; }
/// <summary>
/// Gets signature method.
/// </summary>
public OAuthParameter SignatureMethod { get; private set; }
/// <summary>
/// Gets consumer key.
/// </summary>
public OAuthParameter ConsumerKey { get; private set; }
/// <summary>
/// Gets consumer secret.
/// </summary>
public OAuthParameter ConsumerSecret { get; private set; }
/// <summary>
/// Gets access token.
/// </summary>
public OAuthParameter Token { get; private set; }
/// <summary>
/// Gets access token secret.
/// </summary>
public OAuthParameter TokenSecret { get; private set; }
/// <summary>
/// Gets signature getter.
/// </summary>
public OAuthParameter Signature => new OAuthParameter("oauth_signature", GenerateSignature());
/// <summary>
/// Gets authorization header getter.
/// </summary>
public string AuthorizationHeader => GenerateAuthorizationHeader();
/// <summary>
/// Initializes a new instance of the <see cref="TwitterOAuthRequestBuilder"/> class.
/// Authorization request builder.
/// </summary>
/// <param name="requestUri">Request Uri.</param>
/// <param name="tokens">Tokens to form request.</param>
/// <param name="signatureManager">Signature manager to sign the OAuth request</param>
/// <param name="method">Method to use with request.</param>
public TwitterOAuthRequestBuilder(Uri requestUri, TwitterOAuthTokens tokens, ISignatureManager signatureManager, string method = "GET")
{
_signatureManager = signatureManager;
Verb = method;
RequestUriWithoutQuery = new Uri(requestUri.AbsoluteWithoutQuery());
if (!string.IsNullOrEmpty(requestUri.Query))
{
QueryParams = requestUri.GetQueryParams()
.Select(p => new OAuthParameter(p.Key, Uri.UnescapeDataString(p.Value)))
.ToList();
}
else
{
QueryParams = new List<OAuthParameter>();
}
EncodedRequestUri = GetEncodedUri(requestUri, QueryParams);
Version = new OAuthParameter("oauth_version", "1.0");
Nonce = new OAuthParameter("oauth_nonce", GenerateNonce());
Timestamp = new OAuthParameter("oauth_timestamp", GenerateTimeStamp());
SignatureMethod = new OAuthParameter("oauth_signature_method", "HMAC-SHA1");
ConsumerKey = new OAuthParameter("oauth_consumer_key", tokens.ConsumerKey);
ConsumerSecret = new OAuthParameter("oauth_consumer_secret", tokens.ConsumerSecret);
Token = new OAuthParameter("oauth_token", tokens.AccessToken);
TokenSecret = new OAuthParameter("oauth_token_secret", tokens.AccessTokenSecret);
}
/// <summary>
/// Get encoded Uri.
/// </summary>
/// <param name="requestUri">Request uri.</param>
/// <param name="parameters">List of parameters.</param>
/// <returns>Encoded Uri.</returns>
private static Uri GetEncodedUri(Uri requestUri, IEnumerable<OAuthParameter> parameters)
{
StringBuilder requestParametersBuilder = new StringBuilder(requestUri.AbsoluteWithoutQuery());
var oAuthParameters = parameters as OAuthParameter[] ?? parameters.ToArray();
if (oAuthParameters.Any())
{
requestParametersBuilder.Append("?");
foreach (var queryParam in oAuthParameters)
{
requestParametersBuilder.AppendFormat("{0}&", queryParam.ToString());
}
requestParametersBuilder.Remove(requestParametersBuilder.Length - 1, 1);
}
return new Uri(requestParametersBuilder.ToString());
}
/// <summary>
/// Generate nonce.
/// </summary>
/// <returns>String nonce.</returns>
private static string GenerateNonce()
{
return new Random()
.Next(123400, int.MaxValue)
.ToString("X", CultureInfo.InvariantCulture);
}
/// <summary>
/// Generate timestamp string.
/// </summary>
/// <returns>Timestamp string.</returns>
private static string GenerateTimeStamp()
{
TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
return Convert.ToInt64(ts.TotalSeconds, CultureInfo.CurrentCulture).ToString(CultureInfo.CurrentCulture);
}
/// <summary>
/// Generate signature.
/// </summary>
/// <returns>Generated signature string.</returns>
private string GenerateSignature()
{
string signatureBaseString = string.Format(
CultureInfo.InvariantCulture,
"{2}&{0}&{1}",
OAuthEncoder.UrlEncode(RequestUriWithoutQuery.Normalize()),
OAuthEncoder.UrlEncode(GetSignParameters()),
Verb);
string key = string.Format(
CultureInfo.InvariantCulture,
"{0}&{1}",
OAuthEncoder.UrlEncode(ConsumerSecret.Value),
OAuthEncoder.UrlEncode(TokenSecret.Value));
return _signatureManager.GetSignature(signatureBaseString, key);
}
/// <summary>
/// Generate authorization header.
/// </summary>
/// <returns>Generated authorization header string.</returns>
private string GenerateAuthorizationHeader()
{
StringBuilder authHeaderBuilder = new StringBuilder();
authHeaderBuilder.AppendFormat("OAuth realm=\"{0}\",", Realm);
authHeaderBuilder.Append(string.Join(",", GetAuthHeaderParameters().OrderBy(p => p.Key).Select(p => p.ToString(true)).ToArray()));
authHeaderBuilder.AppendFormat(",{0}", Signature.ToString(true));
return authHeaderBuilder.ToString();
}
/// <summary>
/// Get list of sign parameters.
/// </summary>
/// <returns>List of sign parameters.</returns>
private IEnumerable<OAuthParameter> GetSignParameters()
{
foreach (var queryParam in QueryParams)
{
yield return queryParam;
}
yield return Version;
yield return Nonce;
yield return Timestamp;
yield return SignatureMethod;
yield return ConsumerKey;
yield return Token;
}
/// <summary>
/// Get list of auth header parameters.
/// </summary>
/// <returns>List of auth header parameters.</returns>
private IEnumerable<OAuthParameter> GetAuthHeaderParameters()
{
yield return Version;
yield return Nonce;
yield return Timestamp;
yield return SignatureMethod;
yield return ConsumerKey;
yield return Token;
}
}
}

Просмотреть файл

@ -1,33 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Net;
using System.Net.Http;
namespace Microsoft.Toolkit.Services.Twitter
{
/// <summary>
/// Twitter Oauth request extensions to add utilities for internal use.
/// </summary>
internal static class TwitterOAuthRequestExtensions
{
public static void ThrowIfNotValid(this HttpResponseMessage response)
{
if (response.StatusCode == HttpStatusCode.NotFound)
{
throw new UserNotFoundException();
}
if ((int)response.StatusCode == 429)
{
throw new TooManyRequestsException();
}
if (response.StatusCode == HttpStatusCode.Unauthorized)
{
throw new OAuthKeysRevokedException();
}
}
}
}

Просмотреть файл

@ -1,37 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace Microsoft.Toolkit.Services.Twitter
{
/// <summary>
/// OAuth token types
/// </summary>
public enum TwitterOAuthTokenType
{
/// <summary>
/// Request or access token
/// </summary>
OAuthRequestOrAccessToken,
/// <summary>
/// Request or access token secret
/// </summary>
OAuthRequestOrAccessTokenSecret,
/// <summary>
/// Verifier
/// </summary>
OAuthVerifier,
/// <summary>
/// Callback confirmed
/// </summary>
OAuthCallbackConfirmed,
/// <summary>
/// Screen name
/// </summary>
ScreenName
}
}

Просмотреть файл

@ -1,47 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace Microsoft.Toolkit.Services.Twitter
{
/// <summary>
/// Twitter OAuth tokens.
/// </summary>
public class TwitterOAuthTokens
{
/// <summary>
/// Gets or sets consumer Key.
/// </summary>
public string ConsumerKey { get; set; }
/// <summary>
/// Gets or sets consumer Secret.
/// </summary>
public string ConsumerSecret { get; set; }
/// <summary>
/// Gets or sets access token.
/// </summary>
public string AccessToken { get; set; }
/// <summary>
/// Gets or sets access token Secret.
/// </summary>
public string AccessTokenSecret { get; set; }
/// <summary>
/// Gets or sets access Request Token.
/// </summary>
public string RequestToken { get; set; }
/// <summary>
/// Gets or sets access Request Token Secret.
/// </summary>
public string RequestTokenSecret { get; set; }
/// <summary>
/// Gets or sets callback Uri.
/// </summary>
public string CallbackUri { get; set; }
}
}

Просмотреть файл

@ -1,41 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Collections.Generic;
using System.Text.Json;
namespace Microsoft.Toolkit.Services.Twitter
{
/// <summary>
/// Twitter Parser.
/// </summary>
/// <typeparam name="T">Type to parse in to.</typeparam>
public class TwitterParser<T> : Toolkit.Parsers.IParser<T>
where T : Toolkit.Parsers.SchemaBase
{
/// <summary>
/// Parse string data into strongly typed list.
/// </summary>
/// <param name="data">Input string.</param>
/// <returns>List of strongly typed objects.</returns>
IEnumerable<T> Toolkit.Parsers.IParser<T>.Parse(string data)
{
if (string.IsNullOrEmpty(data))
{
return null;
}
try
{
return JsonSerializer.Deserialize<List<T>>(data);
}
catch (JsonException)
{
List<T> items = new List<T>();
items.Add(JsonSerializer.Deserialize<T>(data));
return items;
}
}
}
}

Просмотреть файл

@ -1,62 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Text.Json.Serialization;
namespace Microsoft.Toolkit.Services.Twitter
{
/// <summary>
/// Twitter Place
/// </summary>
public class TwitterPlace
{
/// <summary>
/// Gets or sets the ID of the place
/// </summary>
[JsonPropertyName("id")]
public string Id { get; set; }
/// <summary>
/// Gets or sets the URL of additional place metadata
/// </summary>
[JsonPropertyName("url")]
public string Url { get; set; }
/// <summary>
/// Gets or sets the place type.
/// </summary>
[JsonPropertyName("place_type")]
public string PlaceType { get; set; }
/// <summary>
/// Gets or sets the place name.
/// </summary>
[JsonPropertyName("name")]
public string Name { get; set; }
/// <summary>
/// Gets or sets the full, human-readable place name.
/// </summary>
[JsonPropertyName("full_name")]
public string FullName { get; set; }
/// <summary>
/// Gets or sets the shortened country code (e.g. US) for the place.
/// </summary>
[JsonPropertyName("country_code")]
public string CountryCode { get; set; }
/// <summary>
/// Gets or sets the name of the country for the place.
/// </summary>
[JsonPropertyName("country")]
public string Country { get; set; }
/// <summary>
/// Gets or sets the bounding box coordinates of a location.
/// </summary>
[JsonPropertyName("bounding_box")]
public TwitterPlaceBoundingBox BoundingBox { get; set; }
}
}

Просмотреть файл

@ -1,45 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Collections.Generic;
using System.Text.Json.Serialization;
namespace Microsoft.Toolkit.Services.Twitter
{
/// <summary>
/// Twitter Place Bounding Box
/// </summary>
public class TwitterPlaceBoundingBox
{
/// <summary>
/// Gets or sets the bounding box coordinates of the tweet's geolocation data.
/// </summary>
[JsonPropertyName("coordinates")]
public List<List<float[]>> Coordinates { get; set; }
/// <summary>
/// Gets or sets the coordinate type. Polygon for a bounding box, Point for an exact coordinate.
/// </summary>
[JsonPropertyName("type")]
public string Type { get; set; }
/// <summary>
/// Gets the coordinates array of the tweet's geolocation data
/// </summary>
public List<float[]> CoordinatesArray
{
get
{
List<float[]> result = null;
if (Coordinates != null)
{
result = Coordinates[0];
}
return result;
}
}
}
}

Просмотреть файл

@ -1,53 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Globalization;
using System.Text.Json.Serialization;
namespace Microsoft.Toolkit.Services.Twitter
{
/// <summary>
/// Twitter poll object containing poll data.
/// </summary>
public class TwitterPoll
{
/// <summary>
/// Gets or sets poll questions.
/// </summary>
[JsonPropertyName("options")]
public TwitterPollOptions[] Options { get; set; }
/// <summary>
/// Gets or sets end timestamp as a string.
/// </summary>
[JsonPropertyName("end_datetime")]
public string EndDateTime { get; set; }
/// <summary>
/// Gets or sets duration of the poll in minutes.
/// </summary>
[JsonPropertyName("duration_minutes")]
public string DurationMinutes { get; set; }
/// <summary>
/// Gets end timestamp as a DateTime object.
/// </summary>
public DateTime PollEnd
{
get { return FormatDate(EndDateTime); }
}
private DateTime FormatDate(string input)
{
DateTime formattedDateTime;
if (!DateTime.TryParseExact(input, "ddd MMM dd HH:mm:ss zzzz yyyy", CultureInfo.InvariantCulture, DateTimeStyles.None, out formattedDateTime))
{
formattedDateTime = DateTime.Today;
}
return formattedDateTime;
}
}
}

Просмотреть файл

@ -1,26 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Text.Json.Serialization;
namespace Microsoft.Toolkit.Services.Twitter
{
/// <summary>
/// Twitter poll options object containing poll questions.
/// </summary>
public class TwitterPollOptions
{
/// <summary>
/// Gets or sets int value of the poll position.
/// </summary>
[JsonPropertyName("position")]
public int Position { get; set; }
/// <summary>
/// Gets or sets text of the poll question.
/// </summary>
[JsonPropertyName("text")]
public string Text { get; set; }
}
}

Просмотреть файл

@ -1,32 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace Microsoft.Toolkit.Services.Twitter
{
/// <summary>
/// Type of Twitter Query.
/// </summary>
public enum TwitterQueryType
{
/// <summary>
/// Home
/// </summary>
Home,
/// <summary>
/// User
/// </summary>
User,
/// <summary>
/// Search
/// </summary>
Search,
/// <summary>
/// Custom
/// </summary>
Custom
}
}

Просмотреть файл

@ -1,33 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
namespace Microsoft.Toolkit.Services.Twitter
{
/// <summary>
/// Twitter Search Parser.
/// </summary>
public class TwitterSearchParser : Parsers.IParser<Tweet>
{
/// <summary>
/// Parse string into strong typed list.
/// </summary>
/// <param name="data">Input string.</param>
/// <returns>Strong typed list.</returns>
public IEnumerable<Tweet> Parse(string data)
{
if (string.IsNullOrEmpty(data))
{
return null;
}
var result = JsonSerializer.Deserialize<TwitterSearchResult>(data);
return result.Statuses.ToList();
}
}
}

Просмотреть файл

@ -1,17 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace Microsoft.Toolkit.Services.Twitter
{
/// <summary>
/// Twitter Search Results.
/// </summary>
internal class TwitterSearchResult
{
/// <summary>
/// Gets or sets array of timeline statuses.
/// </summary>
public Tweet[] Statuses { get; set; }
}
}

Просмотреть файл

@ -1,440 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Toolkit.Services.Core;
#if WINRT
using Microsoft.Toolkit.Services.PlatformSpecific.Uwp;
using Windows.Storage.Streams;
#endif
#if NET462
using Microsoft.Toolkit.Services.PlatformSpecific.NetFramework;
#endif
namespace Microsoft.Toolkit.Services.Twitter
{
/// <summary>
/// Class for connecting to Twitter.
/// </summary>
public class TwitterService
{
/// <summary>
/// Private field for TwitterDataProvider.
/// </summary>
private TwitterDataProvider twitterDataProvider;
/// <summary>
/// Field for tracking oAuthTokens.
/// </summary>
private TwitterOAuthTokens tokens;
private IPasswordManager passwordManager;
private IStorageManager storageManager;
private IAuthenticationBroker authenticationBroker;
private ISignatureManager signatureManager;
/// <summary>
/// Field for tracking initialization status.
/// </summary>
private bool isInitialized;
/// <summary>
/// Initializes a new instance of the <see cref="TwitterService"/> class.
/// </summary>
public TwitterService()
{
}
/// <summary>
/// Private singleton field.
/// </summary>
private static TwitterService instance;
/// <summary>
/// Gets public singleton property.
/// </summary>
public static TwitterService Instance => instance ?? (instance = new TwitterService());
/// <summary>
/// Gets the current logged in user screen name.
/// </summary>
public string UserScreenName => Provider.UserScreenName;
/// <summary>
/// Initialize underlying provider with relevant token information.
/// </summary>
/// <param name="consumerKey">Consumer key.</param>
/// <param name="consumerSecret">Consumer secret.</param>
/// <param name="callbackUri">Callback URI. Has to match callback URI defined at apps.twitter.com (can be arbitrary).</param>
/// <param name="authenticationBroker">Authentication result interface.</param>
/// <param name="passwordManager">Password Manager interface, store the password.</param>
/// <param name="storageManager">Storage Manager interface</param>
/// <param name="signatureManager">Signature manager to sign the OAuth request</param>
/// <returns>Success or failure.</returns>
public bool Initialize(string consumerKey, string consumerSecret, string callbackUri, IAuthenticationBroker authenticationBroker, IPasswordManager passwordManager, IStorageManager storageManager, ISignatureManager signatureManager)
{
if (string.IsNullOrEmpty(consumerKey))
{
throw new ArgumentNullException(nameof(consumerKey));
}
if (string.IsNullOrEmpty(consumerSecret))
{
throw new ArgumentNullException(nameof(consumerSecret));
}
if (string.IsNullOrEmpty(callbackUri))
{
throw new ArgumentNullException(nameof(callbackUri));
}
if (authenticationBroker == null)
{
throw new ArgumentException(nameof(authenticationBroker));
}
if (passwordManager == null)
{
throw new ArgumentException(nameof(passwordManager));
}
if (storageManager == null)
{
throw new ArgumentException(nameof(storageManager));
}
if (signatureManager == null)
{
throw new ArgumentException(nameof(signatureManager));
}
var oAuthTokens = new TwitterOAuthTokens
{
ConsumerKey = consumerKey,
ConsumerSecret = consumerSecret,
CallbackUri = callbackUri
};
return Initialize(oAuthTokens, authenticationBroker, passwordManager, storageManager, signatureManager);
}
/// <summary>
/// Initialize underlying provider with relevant token information.
/// </summary>
/// <param name="oAuthTokens">Token instance.</param>
/// <param name="authenticationBroker">Authentication result interface.</param>
/// <param name="passwordManager">Password Manager interface, store the password.</param>
/// <param name="storageManager">Storage Manager interface</param>
/// <param name="signatureManager">Signature manager to sign the OAuth request</param>
/// <returns>Success or failure.</returns>
public bool Initialize(TwitterOAuthTokens oAuthTokens, IAuthenticationBroker authenticationBroker, IPasswordManager passwordManager, IStorageManager storageManager, ISignatureManager signatureManager)
{
tokens = oAuthTokens ?? throw new ArgumentNullException(nameof(oAuthTokens));
this.authenticationBroker = authenticationBroker ?? throw new ArgumentNullException(nameof(authenticationBroker));
this.passwordManager = passwordManager ?? throw new ArgumentNullException(nameof(passwordManager));
this.storageManager = storageManager ?? throw new ArgumentNullException(nameof(storageManager));
this.signatureManager = signatureManager ?? throw new ArgumentNullException(nameof(signatureManager));
isInitialized = true;
twitterDataProvider = null;
return true;
}
#if WINRT
/// <summary>
/// Initialize underlying provider with relevant token information for UWP.
/// </summary>
/// <param name="consumerKey">Consumer key.</param>
/// <param name="consumerSecret">Consumer secret.</param>
/// <param name="callbackUri">Callback URI. Has to match callback URI defined at apps.twitter.com (can be arbitrary).</param>
/// <returns>Success or failure.</returns>
public bool Initialize(string consumerKey, string consumerSecret, string callbackUri)
{
return Initialize(consumerKey, consumerSecret, callbackUri, new UwpAuthenticationBroker(), new UwpPasswordManager(), new UwpStorageManager(), new UwpSignatureManager());
}
/// <summary>
/// Initialize underlying provider with relevant token information.
/// </summary>
/// <param name="oAuthTokens">Token instance.</param>
/// <returns>Success or failure.</returns>
public bool Initialize(TwitterOAuthTokens oAuthTokens)
{
return Initialize(oAuthTokens, new UwpAuthenticationBroker(), new UwpPasswordManager(), new UwpStorageManager(), new UwpSignatureManager());
}
#endif
#if NET462
/// <summary>
/// Initialize underlying provider with relevant token information for UWP.
/// </summary>
/// <param name="consumerKey">Consumer key.</param>
/// <param name="consumerSecret">Consumer secret.</param>
/// <param name="callbackUri">Callback URI. Has to match callback URI defined at apps.twitter.com (can be arbitrary).</param>
/// <returns>Success or failure.</returns>
public bool Initialize(string consumerKey, string consumerSecret, string callbackUri)
{
return Initialize(consumerKey, consumerSecret, callbackUri, new NetFrameworkAuthenticationBroker(), new NetFrameworkPasswordManager(), new NetFrameworkStorageManager(), new NetFrameworkSignatureManager());
}
/// <summary>
/// Initialize underlying provider with relevant token information.
/// </summary>
/// <param name="oAuthTokens">Token instance.</param>
/// <returns>Success or failure.</returns>
public bool Initialize(TwitterOAuthTokens oAuthTokens)
{
return Initialize(oAuthTokens, new NetFrameworkAuthenticationBroker(), new NetFrameworkPasswordManager(), new NetFrameworkStorageManager(), new NetFrameworkSignatureManager());
}
#endif
/// <summary>
/// Gets a reference to an instance of the underlying data provider.
/// </summary>
public TwitterDataProvider Provider
{
get
{
if (!isInitialized)
{
throw new InvalidOperationException("Provider not initialized.");
}
return twitterDataProvider ?? (twitterDataProvider = new TwitterDataProvider(tokens, authenticationBroker, passwordManager, storageManager, signatureManager));
}
}
/// <summary>
/// Search for specific hash tag.
/// </summary>
/// <param name="hashTag">Hash tag.</param>
/// <param name="maxRecords">Upper record limit.</param>
/// <returns>Returns strongly typed list of results.</returns>
public async Task<IEnumerable<Tweet>> SearchAsync(string hashTag, int maxRecords = 20)
{
if (Provider.LoggedIn)
{
return await Provider.SearchAsync(hashTag, maxRecords, new TwitterSearchParser());
}
var isLoggedIn = await LoginAsync();
if (isLoggedIn)
{
return await SearchAsync(hashTag, maxRecords);
}
return null;
}
/// <summary>
/// Retrieve user data.
/// </summary>
/// <param name="screenName">User screen name or null for current logged user.</param>
/// <returns>Returns user data.</returns>
public async Task<TwitterUser> GetUserAsync(string screenName = null)
{
if (Provider.LoggedIn)
{
return await Provider.GetUserAsync(screenName);
}
var isLoggedIn = await LoginAsync();
if (isLoggedIn)
{
return await GetUserAsync(screenName);
}
return null;
}
/// <summary>
/// Retrieve user timeline data.
/// </summary>
/// <param name="screenName">User screen name.</param>
/// <param name="maxRecords">Upper record limit.</param>
/// <returns>Returns strongly typed list of results.</returns>
public async Task<IEnumerable<Tweet>> GetUserTimeLineAsync(string screenName, int maxRecords = 20)
{
if (Provider.LoggedIn)
{
return await Provider.GetUserTimeLineAsync(screenName, maxRecords, new TweetParser());
}
var isLoggedIn = await LoginAsync();
if (isLoggedIn)
{
return await GetUserTimeLineAsync(screenName, maxRecords);
}
return null;
}
/// <summary>
/// Post a Tweet with associated pictures.
/// </summary>
/// <param name="message">Tweet message.</param>
/// <returns>Returns success or failure of post request.</returns>
public async Task<bool> TweetStatusAsync(string message)
{
return await TweetStatusAsync(new TwitterStatus { Message = message });
}
/// <summary>
/// Post a Tweet with associated pictures.
/// </summary>
/// <param name="message">Tweet message.</param>
/// <param name="pictures">Pictures to attach to the tweet (up to 4).</param>
/// <returns>Returns success or failure of post request.</returns>
public async Task<bool> TweetStatusAsync(string message, params Stream[] pictures)
{
return await TweetStatusAsync(new TwitterStatus { Message = message }, pictures);
}
/// <summary>
/// Post a Tweet with associated pictures.
/// </summary>
/// <param name="status">The tweet information.</param>
/// <returns>Returns success or failure of post request.</returns>
public async Task<bool> TweetStatusAsync(TwitterStatus status)
{
if (Provider.LoggedIn)
{
return await Provider.TweetStatusAsync(status);
}
var isLoggedIn = await LoginAsync();
if (isLoggedIn)
{
return await TweetStatusAsync(status);
}
return false;
}
/// <summary>
/// Post a Tweet with associated pictures.
/// </summary>
/// <param name="status">The tweet information.</param>
/// <param name="pictures">Pictures to attach to the tweet (up to 4).</param>
/// <returns>Returns success or failure of post request.</returns>
public async Task<bool> TweetStatusAsync(TwitterStatus status, params Stream[] pictures)
{
if (pictures.Length > 4)
{
throw new ArgumentOutOfRangeException(nameof(pictures));
}
if (Provider.LoggedIn)
{
return await Provider.TweetStatusAsync(status, pictures);
}
var isLoggedIn = await LoginAsync();
if (isLoggedIn)
{
return await TweetStatusAsync(status, pictures);
}
return false;
}
/// <summary>
/// Request list data from service provider based upon a given config / query.
/// </summary>
/// <param name="config">TwitterDataConfig instance.</param>
/// <param name="maxRecords">Upper limit of records to return. Up to a maximum of 200 per distinct request.</param>
/// <returns>Strongly typed list of data returned from the service.</returns>
public async Task<List<Tweet>> RequestAsync(TwitterDataConfig config, int maxRecords = 20)
{
return await RequestAsync<Tweet>(config, maxRecords);
}
/// <summary>
/// Request list data from service provider based upon a given config / query.
/// </summary>
/// <typeparam name="T">Model type expected back - e.g. Tweet.</typeparam>
/// <param name="config">TwitterDataConfig instance.</param>
/// <param name="maxRecords">Upper limit of records to return. Up to a maximum of 200 per distinct request.</param>
/// <returns>Strongly typed list of data returned from the service.</returns>
public async Task<List<T>> RequestAsync<T>(TwitterDataConfig config, int maxRecords = 20)
where T : Toolkit.Parsers.SchemaBase
{
if (Provider.LoggedIn)
{
List<T> queryResults = new List<T>();
var results = await Provider.LoadDataAsync<T>(config, maxRecords, 0, new TwitterParser<T>());
foreach (var result in results)
{
queryResults.Add(result);
}
return queryResults;
}
var isLoggedIn = await LoginAsync();
if (isLoggedIn)
{
return await RequestAsync<T>(config, maxRecords);
}
return null;
}
/// <summary>
/// Log user in to Twitter.
/// </summary>
/// <returns>Returns success or failure of login attempt.</returns>
public Task<bool> LoginAsync()
{
return Provider.LoginAsync();
}
/// <summary>
/// Log user out of Twitter.
/// </summary>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public Task LogoutAsync()
{
return Provider.LogoutAsync();
}
/// <summary>
/// Open a connection to user's stream service
/// </summary>
/// <param name="callback">Method called each time a tweet arrives</param>
/// <returns>Task</returns>
public async Task StartUserStreamAsync(TwitterStreamCallbacks.TwitterStreamCallback callback)
{
if (Provider.LoggedIn)
{
await Provider.StartUserStreamAsync(new TwitterUserStreamParser(), callback);
return;
}
var isLoggedIn = await LoginAsync();
if (isLoggedIn)
{
await StartUserStreamAsync(callback);
}
}
/// <summary>
/// Close the connection to user's stream service
/// </summary>
public void StopUserStream()
{
Provider.StopStream();
}
}
}

Просмотреть файл

@ -1,112 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Globalization;
namespace Microsoft.Toolkit.Services.Twitter
{
/// <summary>
/// A class for Twitter status other than the pictures
/// NOTE: Can be extended to handle the other pieces of the Twitter Status REST API.
/// https://dev.twitter.com/rest/reference/post/statuses/update
/// Validation COULD be added to the Lat/Long, but since Twitter ignores these if they are invalid then no big deal.
/// </summary>
public class TwitterStatus
{
/// <summary>
/// Gets or sets a value indicating whether the explicit latitude and longitude of the "tweet" message is displayed.
/// NOTE: Whether or not to put a pin on the exact coordinates a Tweet has been sent from.
/// </summary>
public bool DisplayCoordinates { get; set; }
/// <summary>
/// Gets or sets the ID of the original tweet.
/// </summary>
public string InReplyToStatusId { get; set; }
/// <summary>
/// Gets or sets the latitude of the "tweet" message.
/// NOTE: This parameter will be ignored unless it is inside the range -90.0 to +90.0 (North is positive) inclusive.
/// It will also be ignored if there isnt a corresponding long parameter.
/// </summary>
public double? Latitude { get; set; }
/// <summary>
/// Gets or sets the longitude of the "tweet" message.
/// NOTE: The valid ranges for longitude is -180.0 to +180.0 (East is positive) inclusive.
/// This parameter will be ignored if outside that range, if it is not a number, if geo_enabled is disabled,
/// or if there not a corresponding lat parameter.
/// </summary>
public double? Longitude { get; set; }
/// <summary>
/// Gets or sets the text of the Tweet message.
/// </summary>
public string Message { get; set; }
/// <summary>
/// Gets or sets the text of the Tweet message.
/// </summary>
public string PlaceId { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the Tweet contains sensitive content (such as nudity, etc.).
/// </summary>
public bool PossiblySensitive { get; set; }
/// <summary>
/// Gets the request parameters
/// </summary>
public string RequestParameters
{
get
{
string result = $"status={Uri.EscapeDataString(Message)}";
if (Latitude.HasValue && Longitude.HasValue)
{
result = $"{result}&lat={Latitude.Value.ToString(CultureInfo.InvariantCulture)}&long={Longitude.Value.ToString(CultureInfo.InvariantCulture)}";
result = AddRequestParameter(result, "display_coordinates", DisplayCoordinates);
}
result = AddRequestParameter(result, "in_reply_to_status_id", InReplyToStatusId);
result = AddRequestParameter(result, "place_id", PlaceId);
result = AddRequestParameter(result, "possibly_sensitive", PossiblySensitive);
result = AddRequestParameter(result, "trim_user", TrimUser);
return result;
}
}
/// <summary>
/// Gets or sets a value indicating whether the Tweet returned in a timeline will include a user object including only the status authors numerical ID.
/// </summary>
public bool TrimUser { get; set; }
private string AddRequestParameter(string request, string parameterName, bool value)
{
var result = request;
if (value)
{
result = $"{result}&{parameterName}=true";
}
return result;
}
private string AddRequestParameter(string request, string parameterName, string value)
{
var result = request;
if (!string.IsNullOrEmpty(value))
{
result = $"{result}&{parameterName}={Uri.EscapeDataString(value)}";
}
return result;
}
}
}

Просмотреть файл

@ -1,24 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace Microsoft.Toolkit.Services.Twitter
{
/// <summary>
/// Callbacks used for Twitter streams.
/// </summary>
public class TwitterStreamCallbacks
{
/// <summary>
/// Callback converting json to Tweet
/// </summary>
/// <param name="json">Raw Json from Twitter API</param>
public delegate void RawJsonCallback(string json);
/// <summary>
/// Callback returning the parsed tweet
/// </summary>
/// <param name="tweet">Strongly typed tweet</param>
public delegate void TwitterStreamCallback(ITwitterResult tweet);
}
}

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше