add possibility to post logs to discord webhooks

This commit is contained in:
Florian Stinglmayr 2024-08-17 18:38:00 +02:00
parent 0203008202
commit 6a9e4978aa
7 changed files with 299 additions and 65 deletions

32
EliteBGS/DiscordPoster.cs Normal file
View File

@ -0,0 +1,32 @@
using EliteBGS.Util;
using System;
using System.Text;
using System.Net.Http;
using System.Text.Json.Nodes;
namespace EliteBGS;
public class DiscordPoster {
public static readonly int DiscordLimit = 2000;
public static void PostToDiscord(DiscordWebhook webhook, string log) {
JsonObject obj = new();
obj.Add("content", log);
obj.Add("username", "EDBGS");
using (var client = new HttpClient()) {
var content = new StringContent(obj.ToString(), Encoding.UTF8, "application/json");
var response = client.PostAsync(webhook.Webhook, content);
if (response == null) {
throw new Exception("failed to post content to Discord webhook");
}
response.Wait();
var resp = response.Result;
if (!resp.IsSuccessStatusCode) {
throw new Exception(string.Format(
"failed to post content to webhook {0}: {1} / {2}",
webhook.Name, resp.StatusCode, resp.Content.ToString()));
}
}
}
}

View File

@ -60,7 +60,7 @@ public partial class LoadEntriesWindow : Window {
dialog.DefaultExt = ".log"; dialog.DefaultExt = ".log";
dialog.Filter = "Log files (*.log)|*.log|All files (*.*)|*"; dialog.Filter = "Log files (*.log)|*.log|All files (*.*)|*";
var location = config.Global.DefaultJournalLocation; var location = AppConfig.DefaultJournalLocation;
if (Directory.Exists(location)) { if (Directory.Exists(location)) {
dialog.InitialDirectory = location; dialog.InitialDirectory = location;
} }

View File

@ -5,9 +5,13 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:EliteBGS" xmlns:local="clr-namespace:EliteBGS"
xmlns:Util="clr-namespace:EliteBGS.Util" d:DataContext="{d:DesignInstance Type=Util:AppConfig}" x:Name="window" x:Class="EliteBGS.MainWindow" xmlns:Util="clr-namespace:EliteBGS.Util"
d:DataContext="{d:DesignInstance Type=Util:AppConfig}"
x:Name="window"
x:Class="EliteBGS.MainWindow"
mc:Ignorable="d" mc:Ignorable="d"
Title="Elite: Dangerous BGS Helper" Height="520" Width="950" Icon="Salus.ico" Closing="window_Closing"> Title="Elite: Dangerous BGS Helper" Height="620" Width="950" Icon="Salus.ico" Closing="window_Closing"
>
<Window.Resources> <Window.Resources>
<local:MinusFortyFiveConverter x:Key="MinusFortyFiveConverter" /> <local:MinusFortyFiveConverter x:Key="MinusFortyFiveConverter" />
<Style x:Key="StretchingTreeViewStyle" TargetType="TreeViewItem" BasedOn="{StaticResource {x:Type TreeViewItem}}"> <Style x:Key="StretchingTreeViewStyle" TargetType="TreeViewItem" BasedOn="{StaticResource {x:Type TreeViewItem}}">
@ -15,6 +19,23 @@
</Style> </Style>
<local:Report x:Key="ObjectivesBasedOnSystem" /> <local:Report x:Key="ObjectivesBasedOnSystem" />
<Util:Config x:Key="Config" />
<DataTemplate DataType="{x:Type Util:DiscordWebhook}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Text="Name: " Grid.Column="0" Margin="2,0,0,0"/>
<TextBox Text="{Binding Name}" Grid.Column="1"/>
<TextBlock Text="Webhook URL: " Grid.Column="2" Margin="2,0,0,0"/>
<TextBox Text="{Binding Webhook}" Grid.Column="3"/>
</Grid>
</DataTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:SystemObjectives}" ItemsSource="{Binding Path=Objectives}"> <HierarchicalDataTemplate DataType="{x:Type local:SystemObjectives}" ItemsSource="{Binding Path=Objectives}">
<Grid <Grid
@ -162,6 +183,8 @@
<ComboBox x:Name="LogType" VerticalAlignment="Stretch" Margin="0,3,0,3" Width="140" SelectionChanged="LogType_SelectionChanged" /> <ComboBox x:Name="LogType" VerticalAlignment="Stretch" Margin="0,3,0,3" Width="140" SelectionChanged="LogType_SelectionChanged" />
<Separator /> <Separator />
<CheckBox x:Name="SelectAll" Content="Select All" IsChecked="True" Click="SelectAll_Click"/> <CheckBox x:Name="SelectAll" Content="Select All" IsChecked="True" Click="SelectAll_Click"/>
<Separator />
<mah:DropDownButton x:Name="PostToDiscord" Content="Post to Discord" />
</ToolBar> </ToolBar>
<TreeView CheckBox.Checked="TreeView_CheckBox_Updated" <TreeView CheckBox.Checked="TreeView_CheckBox_Updated"
CheckBox.Unchecked="TreeView_CheckBox_Updated" CheckBox.Unchecked="TreeView_CheckBox_Updated"
@ -189,6 +212,7 @@
<RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/> <RowDefinition Height="*"/>
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
@ -244,6 +268,35 @@
<mah:ToggleSwitch x:Name="NoFleetCarrier" Grid.Row="2" Grid.ColumnSpan="2" Content="Ignore transactions done on a Fleet Carrier" Toggled="NoFleetCarrier_Toggled" /> <mah:ToggleSwitch x:Name="NoFleetCarrier" Grid.Row="2" Grid.ColumnSpan="2" Content="Ignore transactions done on a Fleet Carrier" Toggled="NoFleetCarrier_Toggled" />
</Grid> </Grid>
</GroupBox> </GroupBox>
<GroupBox Header="Discord Webhooks" Grid.Row="3" VerticalAlignment="Top" Width="Auto" Grid.ColumnSpan="3" Margin="0,5,0,0">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<DataGrid x:Name="Webhooks"
Grid.Row="0"
ItemsSource="{Binding Webhooks}"
AutoGenerateColumns="False"
ScrollViewer.CanContentScroll="True"
ScrollViewer.VerticalScrollBarVisibility="Auto"
ScrollViewer.HorizontalScrollBarVisibility="Auto"
MaxHeight="100"
MinHeight="100"
KeyUp="Webhooks_KeyUp"
CellEditEnding="Webhooks_CellEditEnding"
>
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding Name}"/>
<DataGridTextColumn Header="Webhook URL" Binding="{Binding Webhook}" Width="*"/>
</DataGrid.Columns>
</DataGrid>
<StackPanel Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Left" Margin="5,10,5,0">
<Button x:Name="AddWebHook" Content="Add" Click="AddWebHook_Click"/>
<Button x:Name="RemoveWebHook" Content="Remove" Click="RemoveWebHook_Click" />
</StackPanel>
</Grid>
</GroupBox>
</Grid> </Grid>
</TabItem> </TabItem>
<TabItem Header="Event Log"> <TabItem Header="Event Log">

View File

@ -46,6 +46,9 @@ public partial class MainWindow : MetroWindow {
/* ignored */ /* ignored */
} }
Webhooks.ItemsSource = Config.Global.Webhooks;
RefreshPostMenu();
foreach (DiscordLogGenerator type in logtypes) { foreach (DiscordLogGenerator type in logtypes) {
LogType.Items.Add(type); LogType.Items.Add(type);
} }
@ -580,4 +583,128 @@ public partial class MainWindow : MetroWindow {
} }
report.SystemObjectives.ForEach(t => { t.IsEnabled = (bool)SelectAll.IsChecked; }); report.SystemObjectives.ForEach(t => { t.IsEnabled = (bool)SelectAll.IsChecked; });
} }
private void AddWebHook_Click(object sender, RoutedEventArgs e) {
Config.Global.Webhooks.Add(new DiscordWebhook {
Name = "Discord Server Name",
Webhook = "..."
});
Webhooks.Items.Refresh();
RefreshPostMenu();
}
private void RemoveWebHook_Click(object sender, RoutedEventArgs e) {
if (Webhooks.SelectedItems.Count <= 0) {
return;
}
var selection = Webhooks.SelectedItems
.OfType<DiscordWebhook>()
.ToList()
;
foreach (var item in selection) {
Config.Global.Webhooks.Remove(item);
}
Webhooks.Items.Refresh();
RefreshPostMenu();
}
private void Webhooks_KeyUp(object sender, KeyEventArgs e) {
DataGridCell cell = e.OriginalSource as DataGridCell;
/* We also get keypresses from DataGridCells that are currently
* editing their content. Filter those out. We don't want to delete
* the row when the user presses DEL while editing the cells content
*/
if (cell == null || cell.IsEditing) {
return;
}
if (e.Key == Key.Delete) {
RemoveWebHook_Click(this, new RoutedEventArgs());
}
}
private void Webhooks_CellEditEnding(object sender, DataGridCellEditEndingEventArgs e) {
try {
Config.SaveGlobal();
} catch (Exception) { }
e.Cancel = false;
RefreshPostMenu();
}
private void RefreshPostMenu() {
MenuItem menu;
PostToDiscord.Items.Clear();
if (Config.Global.Webhooks.Count <= 0) {
PostToDiscord.IsEnabled = false;
} else {
PostToDiscord.IsEnabled = true;
foreach (var item in Config.Global.Webhooks) {
menu = new MenuItem();
menu.Header = item.Name;
menu.Click += DiscordWebhook_Click;
PostToDiscord.Items.Add(menu);
}
PostToDiscord.Items.Add(new Separator());
menu = new MenuItem();
menu.Header = "Post To All";
menu.Click += PostToAll_Click;
PostToDiscord.Items.Add(menu);
}
}
private bool CheckDiscordPostMessageLength() {
var content = DiscordLog.Text;
if (string.IsNullOrEmpty(content)) {
return true;
}
if (content.Length >= DiscordPoster.DiscordLimit) {
MessageBox.Show("The log is too long for discord posting (limit of 2000 characters).",
"Error", MessageBoxButton.OK, MessageBoxImage.Error);
return false;
}
return true;
}
private void PostToDiscordWebhook(DiscordWebhook hook) {
if (string.IsNullOrEmpty(DiscordLog.Text)) {
return;
}
try {
DiscordPoster.PostToDiscord(hook, DiscordLog.Text);
Log(string.Format("successfully posted to discord webhook {0}",
hook.Name));
} catch (Exception ex) {
Log(string.Format("failed to post to discord webhook {0}: {1}",
hook.Name, ex.Message));
}
}
private void DiscordWebhook_Click(object sender, RoutedEventArgs e) {
MenuItem item = sender as MenuItem;
if (item == null) {
return;
}
DiscordWebhook hook = Config.Global.Webhooks
.Find(x => string.Compare(x.Name, item.Header.ToString()) == 0)
;
if (hook == null) {
return;
}
if (!CheckDiscordPostMessageLength()) {
return;
}
PostToDiscordWebhook(hook);
}
private void PostToAll_Click(object sender, RoutedEventArgs e) {
if (!CheckDiscordPostMessageLength()) {
return;
}
foreach (DiscordWebhook hook in Config.Global.Webhooks) {
PostToDiscordWebhook(hook);
}
}
} }

View File

@ -1,13 +1,17 @@
using Newtonsoft.Json; using Newtonsoft.Json;
using System.Collections.Generic;
using System.ComponentModel;
namespace EliteBGS.Util;
namespace EliteBGS.Util {
public class AppConfig { public class AppConfig {
private static readonly string default_journal_location = "%UserProfile%\\Saved Games\\Frontier Developments\\Elite Dangerous"; private static readonly string default_journal_location = "%UserProfile%\\Saved Games\\Frontier Developments\\Elite Dangerous";
private string journal_location = default_journal_location; private string journal_location = default_journal_location;
public string DefaultJournalLocation => default_journal_location;
private string colour = "Amber"; private string colour = "Amber";
private string theme = "Dark"; private string theme = "Dark";
public static string DefaultJournalLocation => default_journal_location;
public string LastUsedDiscordTemplate { get; set; } public string LastUsedDiscordTemplate { get; set; }
public string JournalLocation { public string JournalLocation {
@ -63,9 +67,13 @@ namespace EliteBGS.Util {
/// </summary> /// </summary>
public bool IgnoreFleetCarrier { get; set; } = true; public bool IgnoreFleetCarrier { get; set; } = true;
/// <summary>
/// List of Webhooks configured
/// </summary>
public List<DiscordWebhook> Webhooks { get; set; } = new List<DiscordWebhook>();
[JsonIgnore] [JsonIgnore]
public string FullTheme { public string FullTheme {
get { return Theme + "." + Colour; } get { return Theme + "." + Colour; }
} }
} }
}

View File

@ -2,6 +2,7 @@
using System.Text; using System.Text;
using System.IO; using System.IO;
using Newtonsoft.Json; using Newtonsoft.Json;
using System.ComponentModel;
namespace EliteBGS.Util { namespace EliteBGS.Util {
public class Config { public class Config {

View File

@ -0,0 +1,13 @@
namespace EliteBGS.Util;
public class DiscordWebhook {
/// <summary>
/// Webhook URL
/// </summary>
public string Webhook { get; set; } = string.Empty;
/// <summary>
/// Human readable name for easier identification
/// </summary>
public string Name { get; set; } = string.Empty;
}