109 lines
No EOL
3.6 KiB
C#
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);
|
|
}
|
|
}
|
|
} |