creating notes functionnal
This commit is contained in:
parent
3b08f75204
commit
32c5343722
8 changed files with 126 additions and 35 deletions
7
src/BirdsiteLive.ActivityPub/Models/ActivityCreate.cs
Normal file
7
src/BirdsiteLive.ActivityPub/Models/ActivityCreate.cs
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
namespace BirdsiteLive.ActivityPub
|
||||||
|
{
|
||||||
|
public class ActivityCreate
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
15
src/BirdsiteLive.ActivityPub/Models/ActivityCreateNote.cs
Normal file
15
src/BirdsiteLive.ActivityPub/Models/ActivityCreateNote.cs
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
using System;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace BirdsiteLive.ActivityPub
|
||||||
|
{
|
||||||
|
public class ActivityCreateNote : Activity
|
||||||
|
{
|
||||||
|
public string published { get; set; }
|
||||||
|
public string[] to { get; set; }
|
||||||
|
public string[] cc { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("object")]
|
||||||
|
public Note apObject { get; set; }
|
||||||
|
}
|
||||||
|
}
|
25
src/BirdsiteLive.ActivityPub/Models/Note.cs
Normal file
25
src/BirdsiteLive.ActivityPub/Models/Note.cs
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace BirdsiteLive.ActivityPub
|
||||||
|
{
|
||||||
|
public class Note
|
||||||
|
{
|
||||||
|
public string id { get; set; }
|
||||||
|
public string type { get; } = "Note";
|
||||||
|
public string summary { get; set; }
|
||||||
|
public string inReplyTo { get; set; }
|
||||||
|
public string published { get; set; }
|
||||||
|
public string url { get; set; }
|
||||||
|
public string attributedTo { get; set; }
|
||||||
|
public string[] to { get; set; }
|
||||||
|
public string[] cc { get; set; }
|
||||||
|
public bool sensitive { get; set; }
|
||||||
|
//public string conversation { get; set; }
|
||||||
|
public string content { get; set; }
|
||||||
|
//public Dictionary<string,string> contentMap { get; set; }
|
||||||
|
public string[] attachment { get; set; }
|
||||||
|
public string[] tag { get; set; }
|
||||||
|
//public Dictionary<string, string> replies;
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,7 +12,7 @@ namespace BirdsiteLive.Domain
|
||||||
public interface IActivityPubService
|
public interface IActivityPubService
|
||||||
{
|
{
|
||||||
Task<Actor> GetUser(string objectId);
|
Task<Actor> GetUser(string objectId);
|
||||||
Task<HttpStatusCode> PostDataAsync<T>(T data, string targetHost, string actorUrl);
|
Task<HttpStatusCode> PostDataAsync<T>(T data, string targetHost, string actorUrl, string inbox = null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ActivityPubService : IActivityPubService
|
public class ActivityPubService : IActivityPubService
|
||||||
|
@ -37,19 +37,25 @@ namespace BirdsiteLive.Domain
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<HttpStatusCode> PostDataAsync<T>(T data, string targetHost, string actorUrl)
|
public async Task<HttpStatusCode> PostDataAsync<T>(T data, string targetHost, string actorUrl, string inbox = null)
|
||||||
{
|
{
|
||||||
|
var usedInbox = $"/inbox";
|
||||||
|
if (!string.IsNullOrWhiteSpace(inbox))
|
||||||
|
usedInbox = inbox;
|
||||||
|
|
||||||
var json = JsonConvert.SerializeObject(data);
|
var json = JsonConvert.SerializeObject(data);
|
||||||
|
|
||||||
var date = DateTime.UtcNow.ToUniversalTime();
|
var date = DateTime.UtcNow.ToUniversalTime();
|
||||||
var httpDate = date.ToString("r");
|
var httpDate = date.ToString("r");
|
||||||
var signature = _cryptoService.SignAndGetSignatureHeader(date, actorUrl, targetHost);
|
var signature = _cryptoService.SignAndGetSignatureHeader(date, actorUrl, targetHost, usedInbox);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var client = new HttpClient();
|
var client = new HttpClient();
|
||||||
var httpRequestMessage = new HttpRequestMessage
|
var httpRequestMessage = new HttpRequestMessage
|
||||||
{
|
{
|
||||||
Method = HttpMethod.Post,
|
Method = HttpMethod.Post,
|
||||||
RequestUri = new Uri($"https://{targetHost}/inbox"),
|
RequestUri = new Uri($"https://{targetHost}/{usedInbox}"),
|
||||||
Headers =
|
Headers =
|
||||||
{
|
{
|
||||||
{"Host", targetHost},
|
{"Host", targetHost},
|
||||||
|
|
|
@ -7,7 +7,7 @@ namespace BirdsiteLive.Domain
|
||||||
public interface ICryptoService
|
public interface ICryptoService
|
||||||
{
|
{
|
||||||
string GetUserPem(string id);
|
string GetUserPem(string id);
|
||||||
string SignAndGetSignatureHeader(DateTime date, string actor, string host);
|
string SignAndGetSignatureHeader(DateTime date, string actor, string host, string inbox = null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class CryptoService : ICryptoService
|
public class CryptoService : ICryptoService
|
||||||
|
@ -33,11 +33,15 @@ namespace BirdsiteLive.Domain
|
||||||
/// <param name="actor">in the form of https://domain.io/actor</param>
|
/// <param name="actor">in the form of https://domain.io/actor</param>
|
||||||
/// <param name="host">in the form of domain.io</param>
|
/// <param name="host">in the form of domain.io</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public string SignAndGetSignatureHeader(DateTime date, string actor, string targethost)
|
public string SignAndGetSignatureHeader(DateTime date, string actor, string targethost, string inbox = null)
|
||||||
{
|
{
|
||||||
|
var usedInbox = "/inbox";
|
||||||
|
if (!string.IsNullOrWhiteSpace(inbox))
|
||||||
|
usedInbox = inbox;
|
||||||
|
|
||||||
var httpDate = date.ToString("r");
|
var httpDate = date.ToString("r");
|
||||||
|
|
||||||
var signedString = $"(request-target): post /inbox\nhost: {targethost}\ndate: {httpDate}";
|
var signedString = $"(request-target): post {usedInbox}\nhost: {targethost}\ndate: {httpDate}";
|
||||||
var signedStringBytes = Encoding.UTF8.GetBytes(signedString);
|
var signedStringBytes = Encoding.UTF8.GetBytes(signedString);
|
||||||
var signature = _magicKeyFactory.GetMagicKey().Sign(signedStringBytes);
|
var signature = _magicKeyFactory.GetMagicKey().Sign(signedStringBytes);
|
||||||
var sig64 = Convert.ToBase64String(signature);
|
var sig64 = Convert.ToBase64String(signature);
|
||||||
|
|
|
@ -17,12 +17,14 @@ namespace BirdsiteLive.Controllers
|
||||||
{
|
{
|
||||||
private readonly InstanceSettings _instanceSettings;
|
private readonly InstanceSettings _instanceSettings;
|
||||||
private readonly ICryptoService _cryptoService;
|
private readonly ICryptoService _cryptoService;
|
||||||
|
private readonly IActivityPubService _activityPubService;
|
||||||
|
|
||||||
#region Ctor
|
#region Ctor
|
||||||
public DebugController(InstanceSettings instanceSettings, ICryptoService cryptoService)
|
public DebugController(InstanceSettings instanceSettings, ICryptoService cryptoService, IActivityPubService activityPubService)
|
||||||
{
|
{
|
||||||
_instanceSettings = instanceSettings;
|
_instanceSettings = instanceSettings;
|
||||||
_cryptoService = cryptoService;
|
_cryptoService = cryptoService;
|
||||||
|
_activityPubService = activityPubService;
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
@ -35,7 +37,7 @@ namespace BirdsiteLive.Controllers
|
||||||
public async Task<IActionResult> Follow()
|
public async Task<IActionResult> Follow()
|
||||||
{
|
{
|
||||||
var actor = $"https://{_instanceSettings.Domain}/users/gra";
|
var actor = $"https://{_instanceSettings.Domain}/users/gra";
|
||||||
var targethost = "mamot.fr";
|
var targethost = "mastodon.technology";
|
||||||
var followActivity = new ActivityFollow()
|
var followActivity = new ActivityFollow()
|
||||||
{
|
{
|
||||||
context = "https://www.w3.org/ns/activitystreams",
|
context = "https://www.w3.org/ns/activitystreams",
|
||||||
|
@ -45,36 +47,57 @@ namespace BirdsiteLive.Controllers
|
||||||
apObject = $"https://{targethost}/users/testtest"
|
apObject = $"https://{targethost}/users/testtest"
|
||||||
};
|
};
|
||||||
|
|
||||||
var json = JsonConvert.SerializeObject(followActivity);
|
await _activityPubService.PostDataAsync(followActivity, targethost, actor);
|
||||||
|
|
||||||
var date = DateTime.UtcNow.ToUniversalTime();
|
return View("Index");
|
||||||
var httpDate = date.ToString("r");
|
}
|
||||||
var signature = _cryptoService.SignAndGetSignatureHeader(date, actor, targethost);
|
|
||||||
|
|
||||||
var client = new HttpClient();
|
[HttpPost]
|
||||||
var httpRequestMessage = new HttpRequestMessage
|
public async Task<IActionResult> PostNote()
|
||||||
|
{
|
||||||
|
var username = "gra";
|
||||||
|
var actor = $"https://{_instanceSettings.Domain}/users/{username}";
|
||||||
|
var targetHost = "mastodon.technology";
|
||||||
|
var target = $"{targetHost}/users/testtest";
|
||||||
|
var inbox = $"/users/testtest/inbox";
|
||||||
|
|
||||||
|
var noteGuid = Guid.NewGuid();
|
||||||
|
var noteId = $"https://{_instanceSettings.Domain}/users/{username}/statuses/{noteGuid}";
|
||||||
|
var noteUrl = $"https://{_instanceSettings.Domain}/@{username}/{noteGuid}";
|
||||||
|
|
||||||
|
var to = $"{actor}/followers";
|
||||||
|
var apPublic = "https://www.w3.org/ns/activitystreams#Public";
|
||||||
|
|
||||||
|
var now = DateTime.UtcNow;
|
||||||
|
var nowString = now.ToString("s") + "Z";
|
||||||
|
|
||||||
|
var noteActivity = new ActivityCreateNote()
|
||||||
{
|
{
|
||||||
Method = HttpMethod.Post,
|
context = "https://www.w3.org/ns/activitystreams",
|
||||||
RequestUri = new Uri($"https://{targethost}/inbox"),
|
id = $"{noteId}/activity",
|
||||||
Headers =
|
type = "Create",
|
||||||
|
actor = actor,
|
||||||
|
published = nowString,
|
||||||
|
to = new []{ to },
|
||||||
|
//cc = new [] { apPublic },
|
||||||
|
apObject = new Note()
|
||||||
{
|
{
|
||||||
{"Host", targethost},
|
id = noteId,
|
||||||
{"Date", httpDate},
|
summary = null,
|
||||||
{"Signature", signature}
|
inReplyTo = null,
|
||||||
},
|
published = nowString,
|
||||||
Content = new StringContent(json, Encoding.UTF8, "application/ld+json")
|
url = noteUrl,
|
||||||
|
attributedTo = actor,
|
||||||
|
to = new[] { to },
|
||||||
|
//cc = new [] { apPublic },
|
||||||
|
sensitive = false,
|
||||||
|
content = "<p>Woooot</p>",
|
||||||
|
attachment = new string[0],
|
||||||
|
tag = new string[0]
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
try
|
await _activityPubService.PostDataAsync(noteActivity, targetHost, actor, inbox);
|
||||||
{
|
|
||||||
var response = await client.SendAsync(httpRequestMessage);
|
|
||||||
var re = response.ReasonPhrase;
|
|
||||||
var t = await response.Content.ReadAsStringAsync();
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
|
|
||||||
return View("Index");
|
return View("Index");
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,8 +63,10 @@ namespace BirdsiteLive.Controllers
|
||||||
if (succeeded) return Accepted();
|
if (succeeded) return Accepted();
|
||||||
else return Unauthorized();
|
else return Unauthorized();
|
||||||
break;
|
break;
|
||||||
|
case "Undo":
|
||||||
|
return Accepted();
|
||||||
default:
|
default:
|
||||||
return Ok();
|
return Accepted();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -47,5 +47,14 @@ namespace BirdsiteLive.ActivityPub.Tests
|
||||||
Assert.AreEqual("Follow", data.apObject.type);
|
Assert.AreEqual("Follow", data.apObject.type);
|
||||||
Assert.AreEqual("https://mamot.fr/users/testtest", data.apObject.apObject);
|
Assert.AreEqual("https://mamot.fr/users/testtest", data.apObject.apObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void NoteDeserializationTest()
|
||||||
|
{
|
||||||
|
var json =
|
||||||
|
"{\"@context\":[\"https://www.w3.org/ns/activitystreams\",{\"ostatus\":\"http://ostatus.org#\",\"atomUri\":\"ostatus:atomUri\",\"inReplyToAtomUri\":\"ostatus:inReplyToAtomUri\",\"conversation\":\"ostatus:conversation\",\"sensitive\":\"as:sensitive\",\"toot\":\"http://joinmastodon.org/ns#\",\"votersCount\":\"toot:votersCount\"}],\"id\":\"https://mastodon.technology/users/testtest/statuses/104424839893177182/activity\",\"type\":\"Create\",\"actor\":\"https://mastodon.technology/users/testtest\",\"published\":\"2020-06-29T02:10:04Z\",\"to\":[\"https://mastodon.technology/users/testtest/followers\"],\"cc\":[],\"object\":{\"id\":\"https://mastodon.technology/users/testtest/statuses/104424839893177182\",\"type\":\"Note\",\"summary\":null,\"inReplyTo\":null,\"published\":\"2020-06-29T02:10:04Z\",\"url\":\"https://mastodon.technology/@testtest/104424839893177182\",\"attributedTo\":\"https://mastodon.technology/users/testtest\",\"to\":[\"https://mastodon.technology/users/testtest/followers\"],\"cc\":[],\"sensitive\":false,\"atomUri\":\"https://mastodon.technology/users/testtest/statuses/104424839893177182\",\"inReplyToAtomUri\":null,\"conversation\":\"tag:mastodon.technology,2020-06-29:objectId=34900058:objectType=Conversation\",\"content\":\"<p>test</p>\",\"contentMap\":{\"en\":\"<p>test</p>\"},\"attachment\":[],\"tag\":[],\"replies\":{\"id\":\"https://mastodon.technology/users/testtest/statuses/104424839893177182/replies\",\"type\":\"Collection\",\"first\":{\"type\":\"CollectionPage\",\"next\":\"https://mastodon.technology/users/testtest/statuses/104424839893177182/replies?only_other_accounts=true&page=true\",\"partOf\":\"https://mastodon.technology/users/testtest/statuses/104424839893177182/replies\",\"items\":[]}}}}";
|
||||||
|
|
||||||
|
var data = ApDeserializer.ProcessActivity(json) as ActivityAcceptFollow;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Add table
Reference in a new issue