Iceshrimp.NET/Iceshrimp.Backend/Core/Services/ImportExportService.cs

109 lines
No EOL
3.6 KiB
C#

using System.Text.Json;
using Iceshrimp.Backend.Controllers.Web.Schemas;
using Iceshrimp.Backend.Core.Configuration;
using Iceshrimp.Backend.Core.Database;
using Iceshrimp.Backend.Core.Database.Tables;
using Iceshrimp.Backend.Core.Extensions;
using Iceshrimp.Backend.Core.Middleware;
using Iceshrimp.MfmSharp;
using Iceshrimp.Shared.Configuration;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;
using static Iceshrimp.Backend.Core.Federation.ActivityPub.UserResolver;
namespace Iceshrimp.Backend.Core.Services;
public class ImportExportService(
DatabaseContext db,
ILogger<UserService> logger,
IOptions<Config.InstanceSection> instance,
CacheService cacheSvc,
UserService userSvc,
NoteService noteSvc,
DriveService driveSvc,
ActivityPub.UserResolver userResolver
) : IScopedService
{
public async Task<string> ExportFollowingAsync(User user)
{
var followees = await db.Followings
.Include(p => p.Followee)
.Where(p => p.FollowerId == user.Id)
.Select(p => p.Followee)
.Where(p => !p.IsDeleted && !p.IsSystemUser && p.MovedToUri == null)
.OrderBy(p => p.Host)
.ThenBy(p => p.UsernameLower)
.Select(p => p.GetFqn(instance.Value.AccountDomain))
.ToListAsync();
return string.Join("\n", followees);
}
public async Task ImportFollowingAsync(User user, List<string> fqns)
{
foreach (var fqn in fqns)
{
try
{
var followee = await userResolver.ResolveAsync($"acct:{fqn}", ResolveFlags.Acct);
await userSvc.FollowUserAsync(user, followee);
}
catch (Exception e)
{
logger.LogWarning("Failed to import follow {followee} for user {follower}: {error}",
fqn, user.Id, e);
}
}
await QueryableTimelineExtensions.ResetHeuristicAsync(user, cacheSvc);
}
public async Task ImportNotesAsync(User user, IFormFile file)
{
var notes = await JsonSerializer.DeserializeAsync<List<ExportNote>>(file.OpenReadStream(), JsonSerialization.Options);
if (notes == null)
throw GracefulException.BadRequest("File is not an Iceshrimp note export");
notes = notes.FindAll(p => p is
{
LocalOnly: false,
Poll: null,
Visibility: Note.NoteVisibility.Public or Note.NoteVisibility.Home,
VisibleUserIds: []
});
var importedNotes = new Dictionary<string, Note>();
foreach (var exportNote in notes)
{
if (exportNote.ReplyId != null && !importedNotes.ContainsKey(exportNote.ReplyId))
continue;
if (exportNote.RenoteId != null && !importedNotes.ContainsKey(exportNote.RenoteId))
continue;
var attachments = new List<DriveFile>();
foreach (var exportNoteFile in exportNote.Files)
{
var attachment = await driveSvc.StoreFileAsync(exportNoteFile.Url, user, exportNoteFile.IsSensitive,
exportNoteFile.Comment, exportNoteFile.Type);
if (attachment != null) attachments.Add(attachment);
}
var note = new NoteService.NoteCreationData
{
User = user,
Visibility = exportNote.Visibility,
Text = exportNote.Text?.Replace("@", "@\\"),
Cw = exportNote.Cw,
Reply = exportNote.ReplyId != null ? importedNotes.GetValueOrDefault(exportNote.ReplyId) : null,
Renote = exportNote.RenoteId != null ? importedNotes.GetValueOrDefault(exportNote.RenoteId) : null,
Attachments = attachments.Count != 0 ? attachments : null,
CreatedAt = exportNote.CreatedAt
};
var newNote = await noteSvc.CreateNoteAsync(note);
importedNotes.Add(exportNote.Id, newNote);
}
}
}