2019-06-14 20:48:19 +00:00
using System.Text ;
2020-02-12 14:16:19 +00:00
2020-12-25 11:56:46 +00:00
using Myriad.Rest.Exceptions ;
2021-01-15 10:29:43 +00:00
using Myriad.Rest.Types ;
using Myriad.Rest.Types.Requests ;
2020-12-25 12:58:45 +00:00
using Myriad.Types ;
2020-12-25 11:56:46 +00:00
2019-06-14 20:48:19 +00:00
using Newtonsoft.Json ;
2020-09-20 21:32:57 +00:00
using Newtonsoft.Json.Linq ;
2020-02-12 14:16:19 +00:00
using PluralKit.Core ;
2019-10-05 05:41:00 +00:00
2021-11-27 02:10:56 +00:00
namespace PluralKit.Bot ;
public class ImportExport
2019-06-14 20:48:19 +00:00
{
2021-11-27 02:10:56 +00:00
private readonly HttpClient _client ;
private readonly DataFileService _dataFiles ;
2022-01-22 07:47:47 +00:00
private readonly PrivateChannelService _dmCache ;
2021-11-27 02:10:56 +00:00
private readonly JsonSerializerSettings _settings = new ( )
2019-06-14 20:48:19 +00:00
{
2021-11-27 02:10:56 +00:00
// Otherwise it'll mess up/reformat the ISO strings for ???some??? reason >.>
DateParseHandling = DateParseHandling . None
} ;
2021-08-27 15:03:47 +00:00
2022-01-22 07:47:47 +00:00
public ImportExport ( DataFileService dataFiles , HttpClient client , PrivateChannelService dmCache )
2021-11-27 02:10:56 +00:00
{
_dataFiles = dataFiles ;
_client = client ;
2022-01-22 07:47:47 +00:00
_dmCache = dmCache ;
2021-11-27 02:10:56 +00:00
}
2019-06-14 20:48:19 +00:00
2021-11-27 02:10:56 +00:00
public async Task Import ( Context ctx )
{
2023-01-08 15:32:41 +00:00
var inputUrl = ctx . RemainderOrNull ( ) ? ? ctx . Message . Attachments . FirstOrDefault ( ) ? . Url ;
if ( inputUrl = = null ) throw Errors . NoImportFilePassed ;
2019-06-14 20:48:19 +00:00
2023-01-08 15:32:41 +00:00
if ( ! Core . MiscUtils . TryMatchUri ( inputUrl , out var url ) )
2022-12-06 10:15:20 +00:00
throw Errors . InvalidUrl ;
2021-11-27 02:10:56 +00:00
await ctx . BusyIndicator ( async ( ) = >
{
JObject data ;
try
2021-08-27 15:03:47 +00:00
{
2021-11-27 02:10:56 +00:00
var response = await _client . GetAsync ( url ) ;
if ( ! response . IsSuccessStatusCode )
2021-08-23 20:53:58 +00:00
throw Errors . InvalidImportFile ;
2021-11-27 02:10:56 +00:00
data = JsonConvert . DeserializeObject < JObject > (
await response . Content . ReadAsStringAsync ( ) ,
_settings
) ;
if ( data = = null )
2021-08-23 20:53:58 +00:00
throw Errors . InvalidImportFile ;
2021-11-27 02:10:56 +00:00
}
catch ( InvalidOperationException )
{
// Invalid URL throws this, we just error back out
throw Errors . InvalidImportFile ;
}
catch ( JsonException )
{
throw Errors . InvalidImportFile ;
}
2020-09-20 21:32:57 +00:00
2021-11-27 02:10:56 +00:00
async Task ConfirmImport ( string message )
{
var msg = $"{message}\n\nDo you want to proceed with the import?" ;
if ( ! await ctx . PromptYesNo ( msg , "Proceed" ) )
throw Errors . ImportCancelled ;
}
2021-08-27 15:03:47 +00:00
2021-11-27 02:10:56 +00:00
if ( data . ContainsKey ( "accounts" )
& & data . Value < JArray > ( "accounts" ) . Type ! = JTokenType . Null
& & data . Value < JArray > ( "accounts" ) . Contains ( ctx . Author . Id . ToString ( ) ) )
2019-06-14 20:48:19 +00:00
{
2021-11-27 02:10:56 +00:00
var msg = $"{Emojis.Warn} You seem to importing a system profile belonging to another account. Are you sure you want to proceed?" ;
if ( ! await ctx . PromptYesNo ( msg , "Import" ) ) throw Errors . ImportCancelled ;
}
2021-08-27 15:03:47 +00:00
2021-11-27 02:10:56 +00:00
var result = await _dataFiles . ImportSystem ( ctx . Author . Id , ctx . System , data , ConfirmImport ) ;
if ( ! result . Success )
if ( result . Message = = null )
throw Errors . InvalidImportFile ;
else
await ctx . Reply (
$"{Emojis.Error} The provided system profile could not be imported: {result.Message}" ) ;
else if ( ctx . System = = null )
// We didn't have a system prior to importing, so give them the new system's ID
await ctx . Reply (
$"{Emojis.Success} PluralKit has created a system for you based on the given file. Your system ID is `{result.CreatedSystem}`. Type `pk;system` for more information." ) ;
else
// We already had a system, so show them what changed
await ctx . Reply (
$"{Emojis.Success} Updated {result.Modified} members, created {result.Added} members. Type `pk;system list` to check!" ) ;
} ) ;
}
2021-08-27 15:03:47 +00:00
2021-11-27 02:10:56 +00:00
public async Task Export ( Context ctx )
{
ctx . CheckSystem ( ) ;
2019-07-14 19:14:16 +00:00
2021-11-27 02:10:56 +00:00
var json = await ctx . BusyIndicator ( async ( ) = >
{
// Make the actual data file
2021-11-30 22:04:42 +00:00
var data = await _dataFiles . ExportSystem ( ctx . System ) ;
2021-11-27 02:10:56 +00:00
return JsonConvert . SerializeObject ( data , Formatting . None ) ;
} ) ;
2021-01-15 10:29:43 +00:00
2021-08-27 15:03:47 +00:00
2021-11-27 02:10:56 +00:00
// Send it as a Discord attachment *in DMs*
var stream = new MemoryStream ( Encoding . UTF8 . GetBytes ( json ) ) ;
try
{
2022-01-22 07:47:47 +00:00
var dm = await _dmCache . GetOrCreateDmChannel ( ctx . Author . Id ) ;
2021-11-27 02:10:56 +00:00
2022-01-22 07:47:47 +00:00
var msg = await ctx . Rest . CreateMessage ( dm ,
2021-11-27 02:10:56 +00:00
new MessageRequest { Content = $"{Emojis.Success} Here you go!" } ,
new [ ] { new MultipartFile ( "system.json" , stream , null ) } ) ;
2022-01-22 07:47:47 +00:00
await ctx . Rest . CreateMessage ( dm , new MessageRequest { Content = $"<{msg.Attachments[0].Url}>" } ) ;
2021-11-27 02:10:56 +00:00
// If the original message wasn't posted in DMs, send a public reminder
if ( ctx . Channel . Type ! = Channel . ChannelType . Dm )
await ctx . Reply ( $"{Emojis.Success} Check your DMs!" ) ;
}
catch ( ForbiddenException )
{
// If user has DMs closed, tell 'em to open them
await ctx . Reply (
$"{Emojis.Error} Could not send the data file in your DMs. Do you have DMs closed?" ) ;
2019-06-14 20:48:19 +00:00
}
}
2021-08-27 15:03:47 +00:00
}