Skip to content

Commit

Permalink
Add oEmbed based Twitter metadata and provider
Browse files Browse the repository at this point in the history
  • Loading branch information
supermomonga committed Jun 25, 2023
1 parent da94374 commit d5d5507
Show file tree
Hide file tree
Showing 12 changed files with 204 additions and 13 deletions.
9 changes: 9 additions & 0 deletions MoEmbed.App/packages.lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,14 @@
"Microsoft.Extensions.FileProviders.Physical": "7.0.0"
}
},
"AngleSharp": {
"type": "Transitive",
"resolved": "1.0.4",
"contentHash": "G8R4C2MEDFQvjUbYz1QIcGfibjsTJnzP0aWy8iQgWWk7eKacYydCNGD3JMhVL0Q5pZQ9RYlqpKNESEU5NpqsRw==",
"dependencies": {
"System.Text.Encoding.CodePages": "7.0.0"
}
},
"HtmlAgilityPack.NetCore": {
"type": "Transitive",
"resolved": "1.5.0.1",
Expand Down Expand Up @@ -895,6 +903,7 @@
"moembed.core": {
"type": "Project",
"dependencies": {
"AngleSharp": "[1.0.4, )",
"HtmlAgilityPack.NetCore": "[1.5.0.1, )",
"Microsoft.AspNetCore": "[2.2.0, )",
"Microsoft.Extensions.Caching.Memory": "[7.0.0, )",
Expand Down
1 change: 0 additions & 1 deletion MoEmbed.CodeGeneration/OEmbedProxyMetadataProviders.csx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ using (var sw = new StreamWriter(Path.Combine(dir, "../MoEmbed.Core/Providers/Ge
sw.WriteLine("{");

var skip = new HashSet<string>() {
"twitter",
"gyazo",
"nanoo" // It conflicts with zhdk.ch.
};
Expand Down
33 changes: 33 additions & 0 deletions MoEmbed.Core.Tests/Models/Metadata/TwitterMetadataTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using MoEmbed.Providers;

using System;

using Xunit;

namespace MoEmbed.Models.Metadata
{
public class TwitterMetadataTest
{
[Theory]
[InlineData(
463440424141459456L,
"US Department of the Interior",
"Sunsets don't get much better than this one over @GrandTetonNPS. #nature #sunset pic.twitter.com/YuKy2rcjyU— US Department of the Interior (@Interior) May 5, 2014"
)]
public void FetchAsyncTest(long tweetId, string expectedDisplayName, string expectedDescription)
{
var uri = $"https://twitter.com/Interior/status/{tweetId}";
var target = new TwitterMetadataProvider().GetMetadata(
new ConsumerRequest(new Uri(uri)));

var data = target.FetchAsync(
new RequestContext(
new MetadataService(),
new ConsumerRequest(new Uri(uri))))
.GetAwaiter().GetResult();

Assert.Equal(expectedDisplayName, data.Title);
Assert.Equal(expectedDescription, data.Description);
}
}
}
45 changes: 45 additions & 0 deletions MoEmbed.Core.Tests/Providers/TwitterMetadataProviderTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using MoEmbed.Models;
using MoEmbed.Models.Metadata;

using System;

using Xunit;

namespace MoEmbed.Providers
{
public sealed class TwitterMetadataProviderTest
{
#region CanHandle

[Theory]
[InlineData("https://twitter.com/Interior/status/463440424141459456")]
public void CanHandleTest_True(string uri)
=> Assert.True(new TwitterMetadataProvider().CanHandle(new ConsumerRequest(new Uri(uri))));

[Theory]
[InlineData("https://rms.sexy/")]
public void CanHandleTest_False(string uri)
=> Assert.False(new TwitterMetadataProvider().CanHandle(new ConsumerRequest(new Uri(uri))));

#endregion CanHandle

#region GetMetadata

[Theory]
[InlineData("https://twitter.com/Interior/status/463440424141459456")]
public void GetMetadataTest_Success(string uri)
{
var m = Assert.IsType<TwitterMetadata>(
new TwitterMetadataProvider().GetMetadata(new ConsumerRequest(new Uri(uri))));
Assert.Equal(uri, m.Url);
Assert.Null(m.Data);
}

[Theory]
[InlineData("https://rms.sexy/")]
public void GetMetadataTest_Null(string uri)
=> Assert.Null(new TwitterMetadataProvider().GetMetadata(new ConsumerRequest(new Uri(uri))));

#endregion GetMetadata
}
}
1 change: 1 addition & 0 deletions MoEmbed.Core/MoEmbed.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
<WarningsAsErrors />
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AngleSharp" Version="1.0.4" />
<PackageReference Include="HtmlAgilityPack.NetCore" Version="1.5.0.1" />
<PackageReference Include="Microsoft.AspNetCore" Version="2.2.0" />
<PackageReference Include="Portable.Xaml" Version="0.25.0" />
Expand Down
14 changes: 10 additions & 4 deletions MoEmbed.Core/Models/Metadata/OEmbedProxyMetadata.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
using System;
using Newtonsoft.Json.Linq;

using Portable.Xaml.Markup;

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Threading.Tasks;
using System.Xml;
using Newtonsoft.Json.Linq;
using Portable.Xaml.Markup;

namespace MoEmbed.Models.Metadata
{
Expand Down Expand Up @@ -143,6 +145,10 @@ protected internal virtual EmbedData CreateEmbedData(Dictionary<string, object>
{
data.AuthorUrl = authorUrl?.ToString();
}
if (values.TryGetValue(HTML, out var html))
{
data.Html = html?.ToString();
}
if (values.TryGetValue(PROVIDER_NAME, out var providerName))
{
data.ProviderName = providerName?.ToString();
Expand All @@ -153,7 +159,7 @@ protected internal virtual EmbedData CreateEmbedData(Dictionary<string, object>
}
if (values.TryGetValue(CACHE_AGE, out var cacheAge))
{
data.CacheAge = (cacheAge as IConvertible).ToInt32(null);
data.CacheAge = (cacheAge as IConvertible).ToInt64(null);
}

values.TryGetValue(TYPE, out var typeObj);
Expand Down
29 changes: 29 additions & 0 deletions MoEmbed.Core/Models/Metadata/TwitterMetadata.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using Portable.Xaml.Markup;

using System;
using System.Collections.Generic;

namespace MoEmbed.Models.Metadata
{
/// <summary>
/// Represents the <see cref="Metadata"/> for the URL of the nicotweet.jp.
/// </summary>
[Serializable]
[ContentProperty(nameof(Data))]
public class TwitterMetadata : OEmbedProxyMetadata
{
/// <inheritdoc/>
protected internal override EmbedData CreateEmbedData(Dictionary<string, object> values)
{
var data = base.CreateEmbedData(values);
data.Title = data.AuthorName ?? "Twitter";


var parser = new AngleSharp.Html.Parser.HtmlParser();
using var doc = parser.ParseDocument(data.Html);
data.Description = doc.QuerySelector("blockquote").TextContent;

return data;
}
}
}
1 change: 1 addition & 0 deletions MoEmbed.Core/Models/ResponseWriterExtension.cs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ public static async Task WriteEmbedDataAsync(this IResponseWriter writer, EmbedD
writer.WritePropertyIfNeeded("description", obj.Description);
writer.WritePropertyIfNeeded("author_name", obj.AuthorName);
writer.WritePropertyIfNeeded("author_url", obj.AuthorUrl);
writer.WritePropertyIfNeeded("html", obj.Html);
writer.WritePropertyIfNeeded("provider_name", obj.ProviderName);
writer.WritePropertyIfNeeded("provider_url", obj.ProviderUrl);
writer.WritePropertyIfNeeded("cache_age", obj.CacheAge);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5737,6 +5737,28 @@ protected override Uri GetProviderUriFor(ConsumerRequest request)
=> GetProviderUriCore("https://twinmotion.unrealengine.com/oembed", request);
}

/// <summary>
/// Handles oEmbed request for <see href="http://www.twitter.com/" />.
/// </summary>
public sealed partial class TwitterMetadataProvider : OEmbedProxyMetadataProvider, IMetadataProvider
{
private static readonly Regex _UriPattern = new Regex(@"^(https://twitter\.com/|https://twitter\.com/.*/status/|https://.*\.twitter\.com/.*/status/)");

/// <inheritdoc />
public override IEnumerable<string> GetSupportedHostNames()
{
yield return "twitter.com";
}

/// <inheritdoc />
public override bool CanHandle(Uri uri)
=> _UriPattern.IsMatch(uri.ToString());

/// <inheritdoc />
protected override Uri GetProviderUriFor(ConsumerRequest request)
=> GetProviderUriCore("https://publish.twitter.com/oembed", request);
}

/// <summary>
/// Handles oEmbed request for <see href="https://typecast.ai" />.
/// </summary>
Expand Down Expand Up @@ -6692,6 +6714,7 @@ internal static IEnumerable<Type> CreateKnownHandlerTypes()
yield return typeof(TuxxBeMetadataProvider);
yield return typeof(TvcfMetadataProvider);
yield return typeof(TwinmotionMetadataProvider);
yield return typeof(TwitterMetadataProvider);
yield return typeof(TypecastMetadataProvider);
yield return typeof(TyplogMetadataProvider);
yield return typeof(UcamMapMetadataProvider);
Expand Down
34 changes: 34 additions & 0 deletions MoEmbed.Core/Providers/TwitterMetadataProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using MoEmbed.Models;
using MoEmbed.Models.Metadata;

namespace MoEmbed.Providers
{
/// <summary>
/// Provides a twitter.com specifiec metadata for the specified consumer request.
/// </summary>
public sealed partial class TwitterMetadataProvider : IMetadataProvider
{

bool IMetadataProvider.SupportsAnyHost
=> false;

bool IMetadataProvider.IsEnabled
=> true;

/// <inheritdoc/>
public new Metadata GetMetadata(ConsumerRequest request)
{
if (!CanHandle(request))
{
return null;
}
var m = new TwitterMetadata
{
Url = request.Url.ToString(),
OEmbedUrl = GetProviderUriFor(request).ToString()
};

return m;
}
}
}
8 changes: 5 additions & 3 deletions MoEmbed.Models.Tests/AttributeTest.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;

using System;
using System.ComponentModel;
using System.Reflection;
using System.Runtime.Serialization;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;

using Xunit;

namespace MoEmbed.Models
Expand Down
19 changes: 14 additions & 5 deletions MoEmbed.Models/EmbedData.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.Serialization;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;

using Portable.Xaml.Markup;

using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.Serialization;

namespace MoEmbed.Models
{
/// <summary>
Expand Down Expand Up @@ -50,6 +52,13 @@ public class EmbedData
[DataMember, JsonProperty("author_url")]
public string AuthorUrl { get; set; }

/// <summary>
/// Gets or sets a HTML of the content.
/// </summary>
[DefaultValue(null)]
[DataMember, JsonProperty("html")]
public string Html { get; set; }

/// <summary>
/// Gets or sets the name of the resource provider.
/// </summary>
Expand All @@ -67,9 +76,9 @@ public class EmbedData
/// <summary>
/// Gets or sets the suggested cache lifetime for this resource, in seconds. Consumers may choose to use this value or not.
/// </summary>
[DefaultValue(86400)]
[DefaultValue(86400L)]
[DataMember, JsonProperty("cache_age")]
public int? CacheAge { get; set; } = 86400;
public long? CacheAge { get; set; } = 86400;

/// <summary>
/// Gets or sets a URL to a thumbnail image representing the resource.
Expand Down

0 comments on commit d5d5507

Please sign in to comment.