added tweet retrieval and display
This commit is contained in:
parent
a947545087
commit
46c18de299
8 changed files with 127 additions and 40 deletions
|
@ -0,0 +1,39 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace BirdsiteLive.ActivityPub.Converters
|
||||||
|
{
|
||||||
|
public class ContextArrayConverter : JsonConverter
|
||||||
|
{
|
||||||
|
public override bool CanWrite { get { return false; } }
|
||||||
|
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
var result = new List<string>();
|
||||||
|
|
||||||
|
var list = serializer.Deserialize<List<object>>(reader);
|
||||||
|
foreach (var l in list)
|
||||||
|
{
|
||||||
|
if (l is string s)
|
||||||
|
result.Add(s);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var str = JsonConvert.SerializeObject(l);
|
||||||
|
result.Add(str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool CanConvert(Type objectType)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,5 @@
|
||||||
using System;
|
using BirdsiteLive.ActivityPub.Converters;
|
||||||
using System.Collections.Generic;
|
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using JsonConverter = Newtonsoft.Json.JsonConverter;
|
|
||||||
|
|
||||||
namespace BirdsiteLive.ActivityPub
|
namespace BirdsiteLive.ActivityPub
|
||||||
{
|
{
|
||||||
|
@ -22,37 +20,4 @@ namespace BirdsiteLive.ActivityPub
|
||||||
public Image icon { get; set; }
|
public Image icon { get; set; }
|
||||||
public Image image { get; set; }
|
public Image image { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ContextArrayConverter : JsonConverter
|
|
||||||
{
|
|
||||||
public override bool CanWrite { get { return false; } }
|
|
||||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
|
||||||
{
|
|
||||||
var result = new List<string>();
|
|
||||||
|
|
||||||
var list = serializer.Deserialize<List<object>>(reader);
|
|
||||||
foreach (var l in list)
|
|
||||||
{
|
|
||||||
if (l is string s)
|
|
||||||
result.Add(s);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var str = JsonConvert.SerializeObject(l);
|
|
||||||
result.Add(str);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result.ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool CanConvert(Type objectType)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,16 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using BirdsiteLive.ActivityPub.Converters;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
namespace BirdsiteLive.ActivityPub
|
namespace BirdsiteLive.ActivityPub
|
||||||
{
|
{
|
||||||
public class Note
|
public class Note
|
||||||
{
|
{
|
||||||
|
[JsonProperty("@context")]
|
||||||
|
[JsonConverter(typeof(ContextArrayConverter))]
|
||||||
|
public string[] context { get; set; } = new[] { "https://www.w3.org/ns/activitystreams" };
|
||||||
|
|
||||||
public string id { get; set; }
|
public string id { get; set; }
|
||||||
public string type { get; } = "Note";
|
public string type { get; } = "Note";
|
||||||
public string summary { get; set; }
|
public string summary { get; set; }
|
||||||
|
|
|
@ -10,6 +10,7 @@ using BirdsiteLive.Common.Settings;
|
||||||
using BirdsiteLive.Cryptography;
|
using BirdsiteLive.Cryptography;
|
||||||
using BirdsiteLive.Twitter.Models;
|
using BirdsiteLive.Twitter.Models;
|
||||||
using Tweetinvi.Core.Exceptions;
|
using Tweetinvi.Core.Exceptions;
|
||||||
|
using Tweetinvi.Models;
|
||||||
|
|
||||||
namespace BirdsiteLive.Domain
|
namespace BirdsiteLive.Domain
|
||||||
{
|
{
|
||||||
|
@ -17,6 +18,7 @@ namespace BirdsiteLive.Domain
|
||||||
{
|
{
|
||||||
Actor GetUser(TwitterUser twitterUser);
|
Actor GetUser(TwitterUser twitterUser);
|
||||||
Task<bool> FollowRequestedAsync(string signature, string method, string path, string queryString, Dictionary<string, string> requestHeaders, ActivityFollow activity);
|
Task<bool> FollowRequestedAsync(string signature, string method, string path, string queryString, Dictionary<string, string> requestHeaders, ActivityFollow activity);
|
||||||
|
Note GetStatus(TwitterUser user, ITweet tweet);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class UserService : IUserService
|
public class UserService : IUserService
|
||||||
|
@ -65,6 +67,39 @@ namespace BirdsiteLive.Domain
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Note GetStatus(TwitterUser user, ITweet tweet)
|
||||||
|
{
|
||||||
|
var actor = GetUser(user);
|
||||||
|
|
||||||
|
var actorUrl = $"{_host}/users/{user.Acct}";
|
||||||
|
var noteId = $"{_host}/users/{user.Acct}/statuses/{tweet.Id}";
|
||||||
|
var noteUrl = $"{_host}/@{user.Acct}/{tweet.Id}";
|
||||||
|
|
||||||
|
var to = $"{actor}/followers";
|
||||||
|
var apPublic = "https://www.w3.org/ns/activitystreams#Public";
|
||||||
|
|
||||||
|
var note = new Note
|
||||||
|
{
|
||||||
|
id = $"{noteId}/activity",
|
||||||
|
|
||||||
|
published = tweet.CreatedAt.ToString("s") + "Z",
|
||||||
|
url = noteUrl,
|
||||||
|
attributedTo = actorUrl,
|
||||||
|
|
||||||
|
//to = new [] {to},
|
||||||
|
//cc = new [] { apPublic },
|
||||||
|
|
||||||
|
to = new[] { apPublic },
|
||||||
|
cc = new[] { to },
|
||||||
|
|
||||||
|
sensitive = false,
|
||||||
|
content = $"<p>{tweet.Text}</p>",
|
||||||
|
attachment = new string[0],
|
||||||
|
tag = new string[0]
|
||||||
|
};
|
||||||
|
return note;
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<bool> FollowRequestedAsync(string signature, string method, string path, string queryString, Dictionary<string, string> requestHeaders, ActivityFollow activity)
|
public async Task<bool> FollowRequestedAsync(string signature, string method, string path, string queryString, Dictionary<string, string> requestHeaders, ActivityFollow activity)
|
||||||
{
|
{
|
||||||
// Validate
|
// Validate
|
||||||
|
@ -91,7 +126,7 @@ namespace BirdsiteLive.Domain
|
||||||
var result = await _activityPubService.PostDataAsync(acceptFollow, targetHost, activity.apObject);
|
var result = await _activityPubService.PostDataAsync(acceptFollow, targetHost, activity.apObject);
|
||||||
return result == HttpStatusCode.Accepted;
|
return result == HttpStatusCode.Accepted;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<bool> ValidateSignature(string actor, string rawSig, string method, string path, string queryString, Dictionary<string, string> requestHeaders)
|
private async Task<bool> ValidateSignature(string actor, string rawSig, string method, string path, string queryString, Dictionary<string, string> requestHeaders)
|
||||||
{
|
{
|
||||||
var signatures = rawSig.Split(',');
|
var signatures = rawSig.Split(',');
|
||||||
|
|
|
@ -3,12 +3,14 @@ using System.Threading.Tasks;
|
||||||
using BirdsiteLive.Common.Settings;
|
using BirdsiteLive.Common.Settings;
|
||||||
using BirdsiteLive.Twitter.Models;
|
using BirdsiteLive.Twitter.Models;
|
||||||
using Tweetinvi;
|
using Tweetinvi;
|
||||||
|
using Tweetinvi.Models;
|
||||||
|
|
||||||
namespace BirdsiteLive.Twitter
|
namespace BirdsiteLive.Twitter
|
||||||
{
|
{
|
||||||
public interface ITwitterService
|
public interface ITwitterService
|
||||||
{
|
{
|
||||||
TwitterUser GetUser(string username);
|
TwitterUser GetUser(string username);
|
||||||
|
ITweet GetTweet(long statusId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class TwitterService : ITwitterService
|
public class TwitterService : ITwitterService
|
||||||
|
@ -40,5 +42,12 @@ namespace BirdsiteLive.Twitter
|
||||||
ProfileBannerURL = user.ProfileBannerURL
|
ProfileBannerURL = user.ProfileBannerURL
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ITweet GetTweet(long statusId)
|
||||||
|
{
|
||||||
|
Auth.SetApplicationOnlyCredentials(_settings.ConsumerKey, _settings.ConsumerSecret, true);
|
||||||
|
var tweet = Tweet.GetTweet(statusId);
|
||||||
|
return tweet;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,7 @@ namespace BirdsiteLive.Controllers
|
||||||
{
|
{
|
||||||
var user = _twitterService.GetUser(id);
|
var user = _twitterService.GetUser(id);
|
||||||
if (user == null) return NotFound();
|
if (user == null) return NotFound();
|
||||||
|
|
||||||
var r = Request.Headers["Accept"].First();
|
var r = Request.Headers["Accept"].First();
|
||||||
if (r.Contains("application/activity+json"))
|
if (r.Contains("application/activity+json"))
|
||||||
{
|
{
|
||||||
|
@ -45,6 +45,31 @@ namespace BirdsiteLive.Controllers
|
||||||
return View(user);
|
return View(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Route("/@{id}/{statusId}")]
|
||||||
|
[Route("/users/{id}/statuses/{statusId}")]
|
||||||
|
public IActionResult Tweet(string id, string statusId)
|
||||||
|
{
|
||||||
|
var r = Request.Headers["Accept"].First();
|
||||||
|
if (r.Contains("application/activity+json"))
|
||||||
|
{
|
||||||
|
if (!long.TryParse(statusId, out var parsedStatusId))
|
||||||
|
return NotFound();
|
||||||
|
|
||||||
|
var tweet = _twitterService.GetTweet(parsedStatusId);
|
||||||
|
if(tweet == null)
|
||||||
|
return NotFound();
|
||||||
|
|
||||||
|
var user = _twitterService.GetUser(id);
|
||||||
|
if (user == null) return NotFound();
|
||||||
|
|
||||||
|
var status = _userService.GetStatus(user, tweet);
|
||||||
|
var jsonApUser = JsonConvert.SerializeObject(status);
|
||||||
|
return Content(jsonApUser, "application/json");
|
||||||
|
}
|
||||||
|
|
||||||
|
return View("Tweet", statusId);
|
||||||
|
}
|
||||||
|
|
||||||
[Route("/users/{id}/inbox")]
|
[Route("/users/{id}/inbox")]
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
public async Task<IActionResult> Inbox()
|
public async Task<IActionResult> Inbox()
|
||||||
|
@ -58,7 +83,7 @@ namespace BirdsiteLive.Controllers
|
||||||
|
|
||||||
switch (activity.type)
|
switch (activity.type)
|
||||||
{
|
{
|
||||||
case "Follow":
|
case "Follow":
|
||||||
var succeeded = await _userService.FollowRequestedAsync(r.Headers["Signature"].First(), r.Method, r.Path, r.QueryString.ToString(), RequestHeaders(r.Headers), activity as ActivityFollow);
|
var succeeded = await _userService.FollowRequestedAsync(r.Headers["Signature"].First(), r.Method, r.Path, r.QueryString.ToString(), RequestHeaders(r.Headers), activity as ActivityFollow);
|
||||||
if (succeeded) return Accepted();
|
if (succeeded) return Accepted();
|
||||||
else return Unauthorized();
|
else return Unauthorized();
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
"environmentVariables": {
|
"environmentVariables": {
|
||||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
},
|
},
|
||||||
"applicationUrl": "https://localhost:5001;http://localhost:5000"
|
"applicationUrl": "http://localhost:5000"
|
||||||
},
|
},
|
||||||
"Docker": {
|
"Docker": {
|
||||||
"commandName": "Docker",
|
"commandName": "Docker",
|
||||||
|
|
8
src/BirdsiteLive/Views/Users/Tweet.cshtml
Normal file
8
src/BirdsiteLive/Views/Users/Tweet.cshtml
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
@{
|
||||||
|
ViewData["Title"] = "Tweet";
|
||||||
|
}
|
||||||
|
|
||||||
|
<div align="center">
|
||||||
|
<blockquote class="twitter-tweet"><p lang="en" dir="ltr">Embedded tweet <a href="https://twitter.com/TwitterSupport/status/@ViewData.Model">Tweet</a></blockquote>
|
||||||
|
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
|
||||||
|
</div>
|
Loading…
Add table
Reference in a new issue