Add some error handling

pull/17/head
Alexey Golub 7 years ago
parent f2178a0445
commit 8afe9852fb

@ -12,13 +12,14 @@ namespace DiscordChatExporter
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
// Services
SimpleIoc.Default.Register<IDataService, DataService>();
SimpleIoc.Default.Register<IExportService, ExportService>();
SimpleIoc.Default.Register<ISettingsService, SettingsService>();
SimpleIoc.Default.Register<IDataService, DataService>(true);
SimpleIoc.Default.Register<IExportService, ExportService>(true);
SimpleIoc.Default.Register<ISettingsService, SettingsService>(true);
// View models
SimpleIoc.Default.Register<IMainViewModel, MainViewModel>();
SimpleIoc.Default.Register<ISettingsViewModel, SettingsViewModel>();
SimpleIoc.Default.Register<IErrorViewModel, ErrorViewModel>(true);
SimpleIoc.Default.Register<IMainViewModel, MainViewModel>(true);
SimpleIoc.Default.Register<ISettingsViewModel, SettingsViewModel>(true);
// Load settings
ServiceLocator.Current.GetInstance<ISettingsService>().Load();
@ -30,6 +31,7 @@ namespace DiscordChatExporter
ServiceLocator.Current.GetInstance<ISettingsService>().Save();
}
public IErrorViewModel ErrorViewModel => ServiceLocator.Current.GetInstance<IErrorViewModel>();
public IMainViewModel MainViewModel => ServiceLocator.Current.GetInstance<IMainViewModel>();
public ISettingsViewModel SettingsViewModel => ServiceLocator.Current.GetInstance<ISettingsViewModel>();
}

@ -84,12 +84,19 @@
<Reference Include="PresentationFramework" />
</ItemGroup>
<ItemGroup>
<Compile Include="Exceptions\UnathorizedException.cs" />
<Compile Include="Messages\ShowErrorMessage.cs" />
<Compile Include="Messages\ShowSettingsMessage.cs" />
<Compile Include="Models\AttachmentType.cs" />
<Compile Include="Models\ChannelChatLog.cs" />
<Compile Include="Models\ChannelType.cs" />
<Compile Include="ViewModels\ErrorViewModel.cs" />
<Compile Include="ViewModels\IErrorViewModel.cs" />
<Compile Include="ViewModels\ISettingsViewModel.cs" />
<Compile Include="ViewModels\SettingsViewModel.cs" />
<Compile Include="Views\ErrorDialog.ammy.cs">
<DependentUpon>ErrorDialog.ammy</DependentUpon>
</Compile>
<Compile Include="Views\SettingsDialog.ammy.cs">
<DependentUpon>SettingsDialog.ammy</DependentUpon>
</Compile>
@ -98,6 +105,11 @@
<Generator>XamlIntelliSenseFileGenerator</Generator>
<DependentUpon>App.ammy</DependentUpon>
</Page>
<Page Include="Views\ErrorDialog.g.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
<DependentUpon>ErrorDialog.ammy</DependentUpon>
</Page>
<Page Include="Views\MainWindow.g.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
@ -150,6 +162,7 @@
<None Include="packages.config">
<SubType>Designer</SubType>
</None>
<None Include="Views\ErrorDialog.ammy" />
<None Include="Views\MainWindow.ammy" />
<None Include="Views\SettingsDialog.ammy" />
</ItemGroup>

@ -0,0 +1,8 @@
using System;
namespace DiscordChatExporter.Exceptions
{
public class UnathorizedException : Exception
{
}
}

@ -0,0 +1,12 @@
namespace DiscordChatExporter.Messages
{
public class ShowErrorMessage
{
public string Message { get; }
public ShowErrorMessage(string message)
{
Message = message;
}
}
}

@ -1,8 +1,10 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using DiscordChatExporter.Exceptions;
using DiscordChatExporter.Models;
using Newtonsoft.Json.Linq;
using Tyrrrz.Extensions;
@ -14,16 +16,30 @@ namespace DiscordChatExporter.Services
private const string ApiRoot = "https://discordapp.com/api/v6";
private readonly HttpClient _httpClient = new HttpClient();
private async Task<string> GetStringAsync(string url)
{
using (var response = await _httpClient.GetAsync(url))
{
// Check status code
if (response.StatusCode.IsEither(HttpStatusCode.Unauthorized, HttpStatusCode.Forbidden))
throw new UnathorizedException();
response.EnsureSuccessStatusCode();
// Get content
return await response.Content.ReadAsStringAsync();
}
}
public async Task<IEnumerable<Guild>> GetGuildsAsync(string token)
{
// Form request url
var url = $"{ApiRoot}/users/@me/guilds?token={token}&limit=100";
// Get response
var response = await _httpClient.GetStringAsync(url);
var content = await GetStringAsync(url);
// Parse
var guilds = JArray.Parse(response).Select(ParseGuild);
var guilds = JArray.Parse(content).Select(ParseGuild);
return guilds;
}
@ -34,10 +50,10 @@ namespace DiscordChatExporter.Services
var url = $"{ApiRoot}/users/@me/channels?token={token}";
// Get response
var response = await _httpClient.GetStringAsync(url);
var content = await GetStringAsync(url);
// Parse
var channels = JArray.Parse(response).Select(ParseChannel);
var channels = JArray.Parse(content).Select(ParseChannel);
return channels;
}
@ -48,10 +64,10 @@ namespace DiscordChatExporter.Services
var url = $"{ApiRoot}/guilds/{guildId}/channels?token={token}";
// Get response
var response = await _httpClient.GetStringAsync(url);
var content = await GetStringAsync(url);
// Parse
var channels = JArray.Parse(response).Select(ParseChannel);
var channels = JArray.Parse(content).Select(ParseChannel);
return channels;
}
@ -71,10 +87,10 @@ namespace DiscordChatExporter.Services
url += $"&before={beforeId}";
// Get response
var response = await _httpClient.GetStringAsync(url);
var content = await GetStringAsync(url);
// Parse
var messages = JArray.Parse(response).Select(ParseMessage);
var messages = JArray.Parse(content).Select(ParseMessage);
// Add messages to list
string currentMessageId = null;

@ -0,0 +1,15 @@
using DiscordChatExporter.Messages;
using GalaSoft.MvvmLight;
namespace DiscordChatExporter.ViewModels
{
public class ErrorViewModel : ViewModelBase, IErrorViewModel
{
public string Message { get; private set; }
public ErrorViewModel()
{
MessengerInstance.Register<ShowErrorMessage>(this, m => Message = m.Message);
}
}
}

@ -0,0 +1,7 @@
namespace DiscordChatExporter.ViewModels
{
public interface IErrorViewModel
{
string Message { get; }
}
}

@ -2,6 +2,7 @@
using System.Diagnostics;
using System.IO;
using System.Linq;
using DiscordChatExporter.Exceptions;
using DiscordChatExporter.Messages;
using DiscordChatExporter.Models;
using DiscordChatExporter.Services;
@ -107,22 +108,29 @@ namespace DiscordChatExporter.ViewModels
// Clear existing
_guildChannelsMap.Clear();
// Get DM channels
try
{
var channels = await _dataService.GetDirectMessageChannelsAsync(_cachedToken);
var guild = new Guild("@me", "Direct Messages", null);
_guildChannelsMap[guild] = channels.ToArray();
}
// Get guild channels
{
var guilds = await _dataService.GetGuildsAsync(_cachedToken);
foreach (var guild in guilds)
// Get DM channels
{
var channels = await _dataService.GetGuildChannelsAsync(_cachedToken, guild.Id);
channels = channels.Where(c => c.Type == ChannelType.GuildTextChat);
var channels = await _dataService.GetDirectMessageChannelsAsync(_cachedToken);
var guild = new Guild("@me", "Direct Messages", null);
_guildChannelsMap[guild] = channels.ToArray();
}
// Get guild channels
{
var guilds = await _dataService.GetGuildsAsync(_cachedToken);
foreach (var guild in guilds)
{
var channels = await _dataService.GetGuildChannelsAsync(_cachedToken, guild.Id);
channels = channels.Where(c => c.Type == ChannelType.GuildTextChat);
_guildChannelsMap[guild] = channels.ToArray();
}
}
}
catch (UnathorizedException)
{
MessengerInstance.Send(new ShowErrorMessage("Failed to authorize. Make sure the token is valid."));
}
AvailableGuilds = _guildChannelsMap.Keys.ToArray();
@ -135,13 +143,13 @@ namespace DiscordChatExporter.ViewModels
IsBusy = true;
// Get safe file names
var safeGroupName = SelectedGuild.Name.Replace(Path.GetInvalidFileNameChars(), '_');
var safeGuildName = SelectedGuild.Name.Replace(Path.GetInvalidFileNameChars(), '_');
var safeChannelName = channel.Name.Replace(Path.GetInvalidFileNameChars(), '_');
// Ask for path
var sfd = new SaveFileDialog
{
FileName = $"{safeGroupName} - {safeChannelName}.html",
FileName = $"{safeGuildName} - {safeChannelName}.html",
Filter = "HTML files (*.html)|*.html|All files (*.*)|*.*",
DefaultExt = "html",
AddExtension = true
@ -152,14 +160,22 @@ namespace DiscordChatExporter.ViewModels
return;
}
// Get messages
var messages = await _dataService.GetChannelMessagesAsync(_cachedToken, channel.Id);
// Export
try
{
// Get messages
var messages = await _dataService.GetChannelMessagesAsync(_cachedToken, channel.Id);
// Create log
var chatLog = new ChannelChatLog(SelectedGuild, channel, messages);
// Create log
var chatLog = new ChannelChatLog(SelectedGuild, channel, messages);
// Export
_exportService.Export(sfd.FileName, chatLog, _settingsService.Theme);
// Export
_exportService.Export(sfd.FileName, chatLog, _settingsService.Theme);
}
catch (UnathorizedException)
{
MessengerInstance.Send(new ShowErrorMessage("Failed to export. You don't have access to that channel."));
}
IsBusy = false;
}

@ -0,0 +1,25 @@
using MaterialDesignThemes.Wpf
UserControl "DiscordChatExporter.Views.ErrorDialog" {
DataContext: bind ErrorViewModel from $resource Container
Width: 250
StackPanel {
// Message
TextBlock {
Margin: 8
FontSize: 16
HorizontalAlignment: Center
TextWrapping: WrapWithOverflow
Text: bind Message
}
// OK
Button {
Command: DialogHost.CloseDialogCommand
Content: "OK"
Margin: 8
Style: resource dyn "MaterialDesignFlatButton"
}
}
}

@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DiscordChatExporter.Views
{
public partial class ErrorDialog
{
public ErrorDialog()
{
InitializeComponent();
}
}
}

@ -17,6 +17,7 @@ namespace DiscordChatExporter.Views
InitializeComponent();
Title += $" v{Assembly.GetExecutingAssembly().GetName().Version}";
Messenger.Default.Register<ShowErrorMessage>(this, m => DialogHost.Show(new ErrorDialog()).Forget());
Messenger.Default.Register<ShowSettingsMessage>(this, m => DialogHost.Show(new SettingsDialog()).Forget());
}

Loading…
Cancel
Save