[backend/federation] Handle quotes
This commit is contained in:
parent
a02af802f1
commit
27b3be774e
5 changed files with 40 additions and 13 deletions
|
@ -23,10 +23,15 @@ public class NoteRenderer(
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
var uri = note.Uri ?? note.GetPublicUri(config.Value);
|
var uri = note.Uri ?? note.GetPublicUri(config.Value);
|
||||||
var renote = note.Renote != null && recurse > 0
|
var renote = note is { Renote: not null, IsQuote: false } && recurse > 0
|
||||||
|
? await RenderAsync(note.Renote, user, accounts, mentions, attachments, likeCounts, likedNotes, 0)
|
||||||
|
: null;
|
||||||
|
var quote = note is { Renote: not null, IsQuote: true } && recurse > 0
|
||||||
? await RenderAsync(note.Renote, user, accounts, mentions, attachments, likeCounts, likedNotes, --recurse)
|
? await RenderAsync(note.Renote, user, accounts, mentions, attachments, likeCounts, likedNotes, --recurse)
|
||||||
: null;
|
: null;
|
||||||
var text = note.Text; //TODO: append quote uri
|
var text = note.Text;
|
||||||
|
if (quote != null && text != null && !text.EndsWith(quote.Url) && !text.EndsWith(quote.Uri))
|
||||||
|
text += $"\n\nRE: {quote.Url}"; //TODO: render as inline quote
|
||||||
|
|
||||||
var likeCount = likeCounts?.GetValueOrDefault(note.Id, 0) ?? await db.NoteLikes.CountAsync(p => p.Note == note);
|
var likeCount = likeCounts?.GetValueOrDefault(note.Id, 0) ?? await db.NoteLikes.CountAsync(p => p.Note == note);
|
||||||
var liked = likedNotes?.Contains(note.Id) ?? await db.NoteLikes.AnyAsync(p => p.Note == note && p.User == user);
|
var liked = likedNotes?.Contains(note.Id) ?? await db.NoteLikes.AnyAsync(p => p.Note == note && p.User == user);
|
||||||
|
@ -87,8 +92,8 @@ public class NoteRenderer(
|
||||||
Account = account,
|
Account = account,
|
||||||
ReplyId = note.ReplyId,
|
ReplyId = note.ReplyId,
|
||||||
ReplyUserId = note.ReplyUserId,
|
ReplyUserId = note.ReplyUserId,
|
||||||
Renote = renote, //TODO: check if it's a pure renote
|
Renote = renote,
|
||||||
Quote = renote, //TODO: see above
|
Quote = quote,
|
||||||
ContentType = "text/x.misskeymarkdown",
|
ContentType = "text/x.misskeymarkdown",
|
||||||
CreatedAt = note.CreatedAt.ToStringMastodon(),
|
CreatedAt = note.CreatedAt.ToStringMastodon(),
|
||||||
EditedAt = note.UpdatedAt?.ToStringMastodon(),
|
EditedAt = note.UpdatedAt?.ToStringMastodon(),
|
||||||
|
|
|
@ -236,9 +236,7 @@ public class ActivityHandlerService(
|
||||||
throw GracefulException.UnprocessableEntity("Invalid or unsupported announce object");
|
throw GracefulException.UnprocessableEntity("Invalid or unsupported announce object");
|
||||||
|
|
||||||
var dbNote = await noteSvc.ResolveNoteAsync(note.Id, note.VerifiedFetch ? note : null);
|
var dbNote = await noteSvc.ResolveNoteAsync(note.Id, note.VerifiedFetch ? note : null);
|
||||||
var renote = await noteSvc.CreateNoteAsync(resolvedActor, announce.GetVisibility(activity.Actor),
|
await noteSvc.CreateNoteAsync(resolvedActor, announce.GetVisibility(activity.Actor), renote: dbNote);
|
||||||
renote: dbNote);
|
|
||||||
await notificationSvc.GenerateRenoteNotification(renote);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -14,6 +14,18 @@ public class ASNote : ASObject
|
||||||
[JC(typeof(VC))]
|
[JC(typeof(VC))]
|
||||||
public string? MkContent { get; set; }
|
public string? MkContent { get; set; }
|
||||||
|
|
||||||
|
[J("https://misskey-hub.net/ns#_misskey_quote")]
|
||||||
|
[JC(typeof(VC))]
|
||||||
|
public string? MkQuote { get; set; }
|
||||||
|
|
||||||
|
[J($"{Constants.ActivityStreamsNs}#quoteUrl")]
|
||||||
|
[JC(typeof(VC))]
|
||||||
|
public string? QuoteUrl { get; set; }
|
||||||
|
|
||||||
|
[J("http://fedibird.com/ns#quoteUri")]
|
||||||
|
[JC(typeof(VC))]
|
||||||
|
public string? QuoteUri { get; set; }
|
||||||
|
|
||||||
[J($"{Constants.ActivityStreamsNs}#content")]
|
[J($"{Constants.ActivityStreamsNs}#content")]
|
||||||
[JC(typeof(VC))]
|
[JC(typeof(VC))]
|
||||||
public string? Content { get; set; }
|
public string? Content { get; set; }
|
||||||
|
|
|
@ -99,6 +99,8 @@ public class NoteService(
|
||||||
await db.SaveChangesAsync();
|
await db.SaveChangesAsync();
|
||||||
eventSvc.RaiseNotePublished(this, note);
|
eventSvc.RaiseNotePublished(this, note);
|
||||||
await notificationSvc.GenerateMentionNotifications(note, mentionedLocalUserIds);
|
await notificationSvc.GenerateMentionNotifications(note, mentionedLocalUserIds);
|
||||||
|
await notificationSvc.GenerateReplyNotifications(note, mentionedLocalUserIds);
|
||||||
|
await notificationSvc.GenerateRenoteNotification(note);
|
||||||
|
|
||||||
if (user.Host != null) return note;
|
if (user.Host != null) return note;
|
||||||
|
|
||||||
|
@ -300,7 +302,7 @@ public class NoteService(
|
||||||
if (actor.IsSuspended)
|
if (actor.IsSuspended)
|
||||||
throw GracefulException.Forbidden("User is suspended");
|
throw GracefulException.Forbidden("User is suspended");
|
||||||
|
|
||||||
//TODO: resolve anything related to the note as well (attachments, emoji, etc)
|
//TODO: resolve emoji
|
||||||
|
|
||||||
var (mentionedUserIds, mentionedLocalUserIds, mentions, remoteMentions, splitDomainMapping) =
|
var (mentionedUserIds, mentionedLocalUserIds, mentions, remoteMentions, splitDomainMapping) =
|
||||||
await ResolveNoteMentionsAsync(note);
|
await ResolveNoteMentionsAsync(note);
|
||||||
|
@ -308,6 +310,8 @@ public class NoteService(
|
||||||
var createdAt = note.PublishedAt?.ToUniversalTime() ??
|
var createdAt = note.PublishedAt?.ToUniversalTime() ??
|
||||||
throw GracefulException.UnprocessableEntity("Missing or invalid PublishedAt field");
|
throw GracefulException.UnprocessableEntity("Missing or invalid PublishedAt field");
|
||||||
|
|
||||||
|
var quoteUrl = note.MkQuote ?? note.QuoteUri ?? note.QuoteUrl;
|
||||||
|
|
||||||
var dbNote = new Note
|
var dbNote = new Note
|
||||||
{
|
{
|
||||||
Id = IdHelpers.GenerateSlowflakeId(createdAt),
|
Id = IdHelpers.GenerateSlowflakeId(createdAt),
|
||||||
|
@ -319,7 +323,8 @@ public class NoteService(
|
||||||
CreatedAt = createdAt,
|
CreatedAt = createdAt,
|
||||||
UserHost = actor.Host,
|
UserHost = actor.Host,
|
||||||
Visibility = note.GetVisibility(actor),
|
Visibility = note.GetVisibility(actor),
|
||||||
Reply = note.InReplyTo?.Id != null ? await ResolveNoteAsync(note.InReplyTo.Id) : null
|
Reply = note.InReplyTo?.Id != null ? await ResolveNoteAsync(note.InReplyTo.Id) : null,
|
||||||
|
Renote = quoteUrl != null ? await ResolveNoteAsync(quoteUrl) : null
|
||||||
};
|
};
|
||||||
|
|
||||||
if (dbNote.Reply != null)
|
if (dbNote.Reply != null)
|
||||||
|
@ -328,6 +333,12 @@ public class NoteService(
|
||||||
dbNote.ReplyUserHost = dbNote.Reply.UserHost;
|
dbNote.ReplyUserHost = dbNote.Reply.UserHost;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (dbNote.Renote != null)
|
||||||
|
{
|
||||||
|
dbNote.RenoteUserId = dbNote.Renote.UserId;
|
||||||
|
dbNote.RenoteUserHost = dbNote.Renote.UserHost;
|
||||||
|
}
|
||||||
|
|
||||||
if (dbNote.Text is { Length: > 100000 })
|
if (dbNote.Text is { Length: > 100000 })
|
||||||
throw GracefulException.UnprocessableEntity("Content cannot be longer than 100.000 characters");
|
throw GracefulException.UnprocessableEntity("Content cannot be longer than 100.000 characters");
|
||||||
if (dbNote.Cw is { Length: > 100000 })
|
if (dbNote.Cw is { Length: > 100000 })
|
||||||
|
@ -369,6 +380,7 @@ public class NoteService(
|
||||||
eventSvc.RaiseNotePublished(this, dbNote);
|
eventSvc.RaiseNotePublished(this, dbNote);
|
||||||
await notificationSvc.GenerateMentionNotifications(dbNote, mentionedLocalUserIds);
|
await notificationSvc.GenerateMentionNotifications(dbNote, mentionedLocalUserIds);
|
||||||
await notificationSvc.GenerateReplyNotifications(dbNote, mentionedLocalUserIds);
|
await notificationSvc.GenerateReplyNotifications(dbNote, mentionedLocalUserIds);
|
||||||
|
await notificationSvc.GenerateRenoteNotification(dbNote);
|
||||||
logger.LogDebug("Note {id} created successfully", dbNote.Id);
|
logger.LogDebug("Note {id} created successfully", dbNote.Id);
|
||||||
return dbNote;
|
return dbNote;
|
||||||
}
|
}
|
||||||
|
|
|
@ -185,7 +185,7 @@ public class NotificationService(
|
||||||
{
|
{
|
||||||
if (note.Renote is not { UserHost: null }) return;
|
if (note.Renote is not { UserHost: null }) return;
|
||||||
if (!note.VisibilityIsPublicOrHome &&
|
if (!note.VisibilityIsPublicOrHome &&
|
||||||
await db.Notes.AnyAsync(p => p.Id == note.Id && p.IsVisibleFor(note.Renote.User)))
|
!await db.Notes.AnyAsync(p => p.Id == note.Id && p.IsVisibleFor(note.Renote.User)))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var notification = new Notification
|
var notification = new Notification
|
||||||
|
@ -195,7 +195,7 @@ public class NotificationService(
|
||||||
Note = note,
|
Note = note,
|
||||||
Notifiee = note.Renote.User,
|
Notifiee = note.Renote.User,
|
||||||
Notifier = note.User,
|
Notifier = note.User,
|
||||||
Type = Notification.NotificationType.Renote
|
Type = note.IsQuote ? Notification.NotificationType.Quote : Notification.NotificationType.Renote
|
||||||
};
|
};
|
||||||
|
|
||||||
await db.AddAsync(notification);
|
await db.AddAsync(notification);
|
||||||
|
|
Loading…
Add table
Reference in a new issue