2019-08-12 15:47:35 +00:00
using System ;
2019-06-14 20:48:19 +00:00
using System.IO ;
using System.Linq ;
using System.Net.Http ;
using System.Text ;
using System.Threading.Tasks ;
2019-07-14 19:14:16 +00:00
using Discord ;
using Discord.Net ;
2019-06-14 20:48:19 +00:00
using Newtonsoft.Json ;
2019-10-05 05:41:00 +00:00
using PluralKit.Bot.CommandSystem ;
2019-06-14 20:48:19 +00:00
namespace PluralKit.Bot.Commands
{
2019-10-05 05:41:00 +00:00
public class ImportExportCommands
2019-06-14 20:48:19 +00:00
{
2019-10-05 05:41:00 +00:00
private DataFileService _dataFiles ;
public ImportExportCommands ( DataFileService dataFiles )
{
_dataFiles = dataFiles ;
}
2019-06-14 20:48:19 +00:00
2019-10-05 05:41:00 +00:00
public async Task Import ( Context ctx )
2019-06-14 20:48:19 +00:00
{
2019-10-05 05:41:00 +00:00
var url = ctx . RemainderOrNull ( ) ? ? ctx . Message . Attachments . FirstOrDefault ( ) ? . Url ;
2019-06-14 20:48:19 +00:00
if ( url = = null ) throw Errors . NoImportFilePassed ;
2019-10-05 05:41:00 +00:00
await ctx . BusyIndicator ( async ( ) = >
2019-06-14 20:48:19 +00:00
{
using ( var client = new HttpClient ( ) )
{
2019-08-12 15:47:35 +00:00
HttpResponseMessage response ;
try
{
response = await client . GetAsync ( url ) ;
}
catch ( InvalidOperationException )
{
// Invalid URL throws this, we just error back out
throw Errors . InvalidImportFile ;
}
2019-06-14 20:48:19 +00:00
if ( ! response . IsSuccessStatusCode ) throw Errors . InvalidImportFile ;
2019-06-15 09:55:11 +00:00
var json = await response . Content . ReadAsStringAsync ( ) ;
2019-06-14 20:48:19 +00:00
2019-06-15 09:55:11 +00:00
var settings = new JsonSerializerSettings
{
MissingMemberHandling = MissingMemberHandling . Error
} ;
DataFileSystem data ;
// TODO: can we clean up this mess?
try
{
data = JsonConvert . DeserializeObject < DataFileSystem > ( json , settings ) ;
}
catch ( JsonException )
{
try
{
var tupperbox = JsonConvert . DeserializeObject < TupperboxProfile > ( json , settings ) ;
if ( ! tupperbox . Valid ) throw Errors . InvalidImportFile ;
var res = tupperbox . ToPluralKit ( ) ;
if ( res . HadGroups | | res . HadMultibrackets | | res . HadIndividualTags )
{
var issueStr =
$"{Emojis.Warn} The following potential issues were detected converting your Tupperbox input file:" ;
if ( res . HadGroups )
issueStr + =
"\n- PluralKit does not support member groups. Members will be imported without groups." ;
if ( res . HadMultibrackets )
issueStr + = "\n- PluralKit does not support members with multiple proxy tags. Only the first pair will be imported." ;
if ( res . HadIndividualTags )
issueStr + =
"\n- PluralKit does not support per-member system tags. Since you had multiple members with distinct tags, tags will not be imported. You can set your system tag using the `pk;system tag <tag>` command later." ;
2019-10-05 05:41:00 +00:00
var msg = await ctx . Reply ( $"{issueStr}\n\nDo you want to proceed with the import?" ) ;
if ( ! await ctx . PromptYesNo ( msg ) ) throw Errors . ImportCancelled ;
2019-06-15 09:55:11 +00:00
}
data = res . System ;
}
catch ( JsonException )
{
throw Errors . InvalidImportFile ;
}
}
if ( ! data . Valid ) throw Errors . InvalidImportFile ;
2019-06-14 20:48:19 +00:00
2019-10-05 05:41:00 +00:00
if ( data . LinkedAccounts ! = null & & ! data . LinkedAccounts . Contains ( ctx . Author . Id ) )
2019-06-14 20:48:19 +00:00
{
2019-10-05 05:41:00 +00:00
var msg = await ctx . Reply ( $"{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 ) ) throw Errors . ImportCancelled ;
2019-06-14 20:48:19 +00:00
}
// If passed system is null, it'll create a new one
// (and that's okay!)
2019-10-05 05:41:00 +00:00
var result = await _dataFiles . ImportSystem ( data , ctx . System , ctx . Author . Id ) ;
2019-10-20 07:16:57 +00:00
if ( ! result . Success )
await ctx . Reply ( $"{Emojis.Error} The provided system profile could not be imported. {result.Message}" ) ;
2019-10-20 07:22:22 +00:00
else if ( ctx . System = = null )
2019-06-14 20:48:19 +00:00
{
2019-10-20 07:22:22 +00:00
// We didn't have a system prior to importing, so give them the new system's ID
2019-10-05 05:41:00 +00:00
await ctx . Reply ( $"{Emojis.Success} PluralKit has created a system for you based on the given file. Your system ID is `{result.System.Hid}`. Type `pk;system` for more information." ) ;
2019-06-14 20:48:19 +00:00
}
else
{
2019-10-20 07:22:22 +00:00
// We already had a system, so show them what changed
2019-10-05 05:41:00 +00:00
await ctx . Reply ( $"{Emojis.Success} Updated {result.ModifiedNames.Count} members, created {result.AddedNames.Count} members. Type `pk;system list` to check!" ) ;
2019-06-14 20:48:19 +00:00
}
}
} ) ;
}
2019-10-05 05:41:00 +00:00
public async Task Export ( Context ctx )
2019-06-14 20:48:19 +00:00
{
2019-10-05 05:41:00 +00:00
ctx . CheckSystem ( ) ;
var json = await ctx . BusyIndicator ( async ( ) = >
2019-06-14 20:48:19 +00:00
{
2019-07-14 19:14:16 +00:00
// Make the actual data file
2019-10-05 05:41:00 +00:00
var data = await _dataFiles . ExportSystem ( ctx . System ) ;
2019-07-14 19:14:16 +00:00
return JsonConvert . SerializeObject ( data , Formatting . None ) ;
2019-06-14 20:48:19 +00:00
} ) ;
2019-07-14 19:14:16 +00:00
// Send it as a Discord attachment *in DMs*
var stream = new MemoryStream ( Encoding . UTF8 . GetBytes ( json ) ) ;
try
{
2019-10-05 05:41:00 +00:00
await ctx . Author . SendFileAsync ( stream , "system.json" , $"{Emojis.Success} Here you go!" ) ;
2019-07-14 19:14:16 +00:00
// If the original message wasn't posted in DMs, send a public reminder
2019-10-05 05:41:00 +00:00
if ( ! ( ctx . Channel is IDMChannel ) )
await ctx . Reply ( $"{Emojis.Success} Check your DMs!" ) ;
2019-07-14 19:14:16 +00:00
}
catch ( HttpException )
{
// If user has DMs closed, tell 'em to open them
2019-10-05 05:41:00 +00:00
await ctx . Reply (
2019-07-14 19:14:16 +00:00
$"{Emojis.Error} Could not send the data file in your DMs. Do you have DMs closed?" ) ;
}
2019-06-14 20:48:19 +00:00
}
}
}