Compare commits
20 Commits
0.4.2
...
1da6f41ec8
| Author | SHA1 | Date | |
|---|---|---|---|
| 1da6f41ec8 | |||
| 2c6eb9190a | |||
| 8a92cac02a | |||
| 4fe77e6946 | |||
| 912e8b602f | |||
| be3bceb880 | |||
| 6cbe54ff73 | |||
| 6fd5bbc582 | |||
| 262182cfaf | |||
| 007b391dc2 | |||
| 9918c7d559 | |||
| 2bf8d9018d | |||
| 8eaf94f634 | |||
| 20adf93d39 | |||
| fd3e5f61cb | |||
| e617c3852b | |||
| 9f013bed38 | |||
| 916afb2348 | |||
| 6eb892151c | |||
| 6a9e4978aa |
@@ -35,10 +35,10 @@ public class Cartographics : Transaction {
|
||||
/* add multi sell and normal ones together */
|
||||
long total =
|
||||
Entries.OfType<MultiSellExplorationDataEntry>()
|
||||
.Sum(x => x.TotalEarnings)
|
||||
.Sum(x => x.Total)
|
||||
+
|
||||
Entries.OfType<SellExplorationDataEntry>()
|
||||
.Sum(x => x.TotalEarnings)
|
||||
.Sum(x => x.Total)
|
||||
;
|
||||
return total;
|
||||
}
|
||||
|
||||
@@ -92,6 +92,13 @@ public class CombatZone : Transaction {
|
||||
get { return string.Compare(Type, CombatZones.AXCombatZone) == 0; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if it is a power combat zone
|
||||
/// </summary>
|
||||
public bool IsPower {
|
||||
get { return string.Compare(Type, CombatZones.PowerCombatZone) == 0; }
|
||||
}
|
||||
|
||||
public override int CompareTo(Transaction? obj) {
|
||||
if (obj == null || obj.GetType() != typeof(CombatZone)) {
|
||||
return -1;
|
||||
|
||||
17
EDPlayerJournal/BGS/Parsers/CommanderParser.cs
Normal file
17
EDPlayerJournal/BGS/Parsers/CommanderParser.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using EDPlayerJournal.BGS;
|
||||
using EDPlayerJournal.Entries;
|
||||
|
||||
internal class CommanderParser : ITransactionParserPart {
|
||||
public void Parse(Entry entry, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) {
|
||||
CommanderEntry commanderEntry = (CommanderEntry)entry;
|
||||
if (commanderEntry != null && !string.IsNullOrEmpty(commanderEntry.FullName)) {
|
||||
if (!context.Commanders.Contains(commanderEntry.FullName)) {
|
||||
context.Commanders.Add(commanderEntry.FullName);
|
||||
}
|
||||
}
|
||||
// A commander entry happens when you log out, and log back in again
|
||||
// for example when switching from Open, to Solo or PG.
|
||||
context.DiscernCombatZone(transactions, entry);
|
||||
context.ResetCombatZone();
|
||||
}
|
||||
}
|
||||
@@ -581,14 +581,25 @@ internal class ReceiveTextParser : ITransactionParserPart {
|
||||
}
|
||||
}
|
||||
|
||||
internal class SelfDestructParser : ITransactionParserPart {
|
||||
public void Parse(Entry entry, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) {
|
||||
context.SelfDestruct = true;
|
||||
}
|
||||
}
|
||||
|
||||
internal class DiedParser : ITransactionParserPart {
|
||||
public void Parse(Entry entry, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) {
|
||||
// Death only matters in ship. On foot you can just redeploy with the dropship.
|
||||
if (context.IsOnFoot) {
|
||||
return;
|
||||
}
|
||||
// You can't complete a combat zone if you die in it. Others might keep it open
|
||||
// for you, but still you will not have completed it unless you jump back in.
|
||||
if (context.SelfDestruct != null && context.SelfDestruct == true) {
|
||||
// Some people just suicide to fast track back to stations after a CZ,
|
||||
// especially since combat bonds don't disappear on death. So count the CZ
|
||||
// on self destruct
|
||||
context.DiscernCombatZone(transactions, entry);
|
||||
context.SelfDestruct = null;
|
||||
}
|
||||
context.ResetCombatZone();
|
||||
// Dying also moves you back to another instance
|
||||
context.LeftInstance();
|
||||
@@ -602,15 +613,6 @@ internal class DropshipDeployParser : ITransactionParserPart {
|
||||
}
|
||||
}
|
||||
|
||||
internal class CommanderParser : ITransactionParserPart {
|
||||
public void Parse(Entry entry, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) {
|
||||
// A commander entry happens when you log out, and log back in again
|
||||
// for example when switching from Open, to Solo or PG.
|
||||
context.DiscernCombatZone(transactions, entry);
|
||||
context.ResetCombatZone();
|
||||
}
|
||||
}
|
||||
|
||||
public class TransactionParser {
|
||||
private static Dictionary<string, ITransactionParserPart> ParserParts { get; } = new()
|
||||
{
|
||||
@@ -639,6 +641,7 @@ public class TransactionParser {
|
||||
{ Events.ReceiveText, new ReceiveTextParser() },
|
||||
{ Events.RedeemVoucher, new RedeemVoucherParser() },
|
||||
{ Events.SearchAndRescue, new SearchAndRescueParser() },
|
||||
{ Events.SelfDestruct, new SelfDestructParser() },
|
||||
{ Events.SellExplorationData, new SellExplorationDataParser() },
|
||||
{ Events.SellMicroResources, new SellMicroResourcesParser() },
|
||||
{ Events.SellOrganicData, new SellOrganicDataParser() },
|
||||
@@ -669,6 +672,11 @@ public class TransactionParser {
|
||||
return Parse(entries, defaultOptions);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// List of commanders seen during parsing.
|
||||
/// </summary>
|
||||
public List<string> Commanders { get; set; } = new();
|
||||
|
||||
public List<Transaction>? Parse(IEnumerable<Entry> entries, TransactionParserOptions options) {
|
||||
TransactionList transactions = new();
|
||||
TransactionParserContext context = new();
|
||||
@@ -686,6 +694,9 @@ public class TransactionParser {
|
||||
transactionParserPart.Parse(entry, context, options, transactions);
|
||||
}
|
||||
|
||||
// Copy out list of commanders seen
|
||||
Commanders = context.Commanders;
|
||||
|
||||
return transactions.ToList();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,11 @@
|
||||
namespace EDPlayerJournal.BGS;
|
||||
|
||||
internal class TransactionParserContext {
|
||||
/// <summary>
|
||||
/// List of commander names seen in the logs. May be empty.
|
||||
/// </summary>
|
||||
public List<string> Commanders { get; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Name of the current system the player is in.
|
||||
/// </summary>
|
||||
@@ -57,6 +62,8 @@ internal class TransactionParserContext {
|
||||
public bool HaveSeenAlliedCorrespondent { get; set; } = false;
|
||||
public bool HaveSeenEnemyCorrespondent { get; set; } = false;
|
||||
|
||||
public bool? SelfDestruct { get; set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// Current Odyssey settlement.
|
||||
/// </summary>
|
||||
@@ -123,16 +130,29 @@ internal class TransactionParserContext {
|
||||
Settlement = null;
|
||||
}
|
||||
|
||||
private bool HadCombatZone() {
|
||||
if (CurrentInstanceType != null &&
|
||||
Instances.IsInstance(CurrentInstanceType, Instances.PowerWarzoneMedium)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (HighestCombatBond == null &&
|
||||
LastRecordedAwardingFaction == null &&
|
||||
HaveSeenAXWarzoneNPC == false &&
|
||||
CurrentInstanceType == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void DiscernCombatZone(TransactionList transactions, Entry e) {
|
||||
string? grade = CombatZones.DifficultyLow;
|
||||
string cztype;
|
||||
ulong highest = HighestCombatBond ?? 0;
|
||||
string? faction = LastRecordedAwardingFaction;
|
||||
|
||||
if (HighestCombatBond == null &&
|
||||
LastRecordedAwardingFaction == null &&
|
||||
HaveSeenAXWarzoneNPC == false &&
|
||||
CurrentInstanceType == null) {
|
||||
if (!HadCombatZone()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -152,7 +172,8 @@ internal class TransactionParserContext {
|
||||
return;
|
||||
}
|
||||
if (LastRecordedAwardingFaction == null &&
|
||||
Instances.IsHumanWarzone(CurrentInstanceType)) {
|
||||
(Instances.IsHumanWarzone(CurrentInstanceType) ||
|
||||
Instances.IsPowerWarzone(CurrentInstanceType))) {
|
||||
transactions.AddIncomplete(new CombatZone(),
|
||||
"Could not discern for whom you fought for, " +
|
||||
"as it seems you haven't killed anyone in the ship combat zone.",
|
||||
@@ -182,6 +203,9 @@ internal class TransactionParserContext {
|
||||
} else if (Instances.IsInstance(CurrentInstanceType, Instances.WarzoneThargoidVeryHigh)) {
|
||||
cztype = CombatZones.AXCombatZone;
|
||||
grade = CombatZones.DifficultyVeryHigh;
|
||||
} else if (Instances.IsInstance(CurrentInstanceType, Instances.PowerWarzoneMedium)) {
|
||||
cztype = CombatZones.PowerCombatZone;
|
||||
grade = CombatZones.DifficultyMedium;
|
||||
} else {
|
||||
transactions.AddIncomplete(new CombatZone(),
|
||||
"Unknown conflict zone difficulty",
|
||||
|
||||
@@ -14,6 +14,11 @@ public class CombatZones {
|
||||
/// </summary>
|
||||
public static readonly string ShipCombatZone = "Ship";
|
||||
|
||||
/// <summary>
|
||||
/// Power combat zones, new in Ascendancy update.
|
||||
/// </summary>
|
||||
public static readonly string PowerCombatZone = "Power";
|
||||
|
||||
/// <summary>
|
||||
/// AX combat zone
|
||||
/// </summary>
|
||||
|
||||
@@ -8,4 +8,13 @@ public class CommanderEntry : Entry {
|
||||
Name = JSON.Value<string>("Name") ?? "";
|
||||
FID = JSON.Value<string>("FID") ?? "";
|
||||
}
|
||||
|
||||
public string FullName {
|
||||
get {
|
||||
if (string.IsNullOrEmpty(Name)) {
|
||||
return string.Empty;
|
||||
}
|
||||
return "CMDR " + Name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ public class Entry {
|
||||
{ Events.ReceiveText, typeof(ReceiveTextEntry) },
|
||||
{ Events.RedeemVoucher, typeof(RedeemVoucherEntry) },
|
||||
{ Events.SearchAndRescue, typeof(SearchAndRescueEntry) },
|
||||
{ Events.SelfDestruct, typeof(SelfDestructEntry) },
|
||||
{ Events.SellExplorationData, typeof(SellExplorationDataEntry) },
|
||||
{ Events.SellMicroResources, typeof(SellMicroResourcesEntry) },
|
||||
{ Events.SellOrganicData, typeof(SellOrganicDataEntry) },
|
||||
|
||||
@@ -32,6 +32,7 @@ public class Events {
|
||||
public static readonly string ReceiveText = "ReceiveText";
|
||||
public static readonly string RedeemVoucher = "RedeemVoucher";
|
||||
public static readonly string SearchAndRescue = "SearchAndRescue";
|
||||
public static readonly string SelfDestruct = "SelfDestruct";
|
||||
public static readonly string SellExplorationData = "SellExplorationData";
|
||||
public static readonly string SellMicroResources = "SellMicroResources";
|
||||
public static readonly string SellOrganicData = "SellOrganicData";
|
||||
|
||||
@@ -1,8 +1,23 @@
|
||||
namespace EDPlayerJournal.Entries;
|
||||
|
||||
public class MultiSellExplorationDataEntry : Entry {
|
||||
protected override void Initialise() {
|
||||
TotalEarnings = (JSON.Value<int?>("TotalEarnings") ?? 0);
|
||||
TotalEarnings = JSON.Value<long?>("TotalEarnings") ?? 0;
|
||||
BaseValue = JSON.Value<long?>("BaseValue") ?? 0;
|
||||
Bonus = JSON.Value<long?>("Bonus") ?? 0;
|
||||
}
|
||||
|
||||
public int TotalEarnings { get; set; } = 0;
|
||||
public long Total {
|
||||
get { return BaseValue + Bonus; }
|
||||
}
|
||||
|
||||
public long BaseValue { get; set; } = 0;
|
||||
|
||||
public long Bonus { get; set; } = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Total Earnings are the actual earnings without bonus. So this
|
||||
/// value is BaseValue minus any percent a crew pilot takes.
|
||||
/// </summary>
|
||||
public long TotalEarnings { get; set; } = 0;
|
||||
}
|
||||
|
||||
5
EDPlayerJournal/Entries/SelfDestructEntry.cs
Normal file
5
EDPlayerJournal/Entries/SelfDestructEntry.cs
Normal file
@@ -0,0 +1,5 @@
|
||||
namespace EDPlayerJournal.Entries {
|
||||
public class SelfDestructEntry : Entry {
|
||||
// Has no data
|
||||
}
|
||||
}
|
||||
@@ -6,8 +6,17 @@ namespace EDPlayerJournal.Entries;
|
||||
public class SellExplorationDataEntry : Entry {
|
||||
public long BaseValue { get; set; }
|
||||
public long Bonus { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Total Earnings are the actual earnings without bonus. So this
|
||||
/// value is BaseValue minus any percent a crew pilot takes.
|
||||
/// </summary>
|
||||
public long TotalEarnings { get; set; }
|
||||
|
||||
public long Total {
|
||||
get { return BaseValue + Bonus; }
|
||||
}
|
||||
|
||||
public List<string> Systems { get; set; } = new List<string>();
|
||||
public List<string> Discovered { get; set; } = new List<string>();
|
||||
|
||||
|
||||
@@ -18,6 +18,11 @@ public class Instances {
|
||||
/// </summary>
|
||||
public static readonly string WarzoneHigh = "$Warzone_PointRace_High";
|
||||
|
||||
/// <summary>
|
||||
/// Medium power play conflict zone, new in PP 2.0 Ascendancy update
|
||||
/// </summary>
|
||||
public static readonly string PowerWarzoneMedium = "$Warzone_Powerplay_Med";
|
||||
|
||||
/// <summary>
|
||||
/// Low Thargoid combat zone
|
||||
/// </summary>
|
||||
@@ -52,8 +57,17 @@ public class Instances {
|
||||
;
|
||||
}
|
||||
|
||||
public static bool IsPowerWarzone(string type) {
|
||||
return
|
||||
IsInstance(type, PowerWarzoneMedium)
|
||||
;
|
||||
}
|
||||
|
||||
public static bool IsWarzone(string type) {
|
||||
return IsHumanWarzone(type) || IsThargoidWarzone(type);
|
||||
return IsHumanWarzone(type) ||
|
||||
IsThargoidWarzone(type) ||
|
||||
IsPowerWarzone(type)
|
||||
;
|
||||
}
|
||||
|
||||
public static bool IsInstance(string type, string instance) {
|
||||
|
||||
@@ -1,5 +1,20 @@
|
||||
# EliteBGS changelog
|
||||
|
||||
## 0.4.4 on ??.??.202?
|
||||
|
||||
* Add support for Power Conflict Zones
|
||||
* Show no inf as "Zero INF", so the Nova Navy bot can parse it properly
|
||||
|
||||
## 0.4.3 on 18.09.2024
|
||||
|
||||
* Add possibility to post log reports to Discord webhooks.
|
||||
Logs are automatically split to fit discord length, and you can choose the
|
||||
name of the Commander to post it as. There are some restrictions, so for
|
||||
ultra long logs the one line log format might be necessary.
|
||||
* Fix cartographics data value by igonring `TotalEarnings`. Total earnings
|
||||
is the same as `BaseValue`, except any percentages taken by crew members
|
||||
is deducted. Actual total value is `BaseValue` plus `Bonus`.
|
||||
|
||||
## 0.4.2 on 02.05.2024
|
||||
|
||||
* Add a bot header for all generated logs that shows the tool version, as
|
||||
|
||||
@@ -198,4 +198,65 @@ public class DiscordLogGenerator {
|
||||
|
||||
return log.ToString().Trim();
|
||||
}
|
||||
|
||||
public virtual string[] SplitLog(string log, int maxcount = 2000) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected string[] SplitLogWithHeader(string log, string header, int maxcount = 2000) {
|
||||
string[] lines = log.Split("\n");
|
||||
List<string> chunks = new();
|
||||
string chunk = string.Empty;
|
||||
|
||||
// Optimisation
|
||||
if (log.Length <= maxcount) {
|
||||
return new string[] { log };
|
||||
}
|
||||
|
||||
// First split the log into its headers
|
||||
// skip first bot header line
|
||||
for (int i = 1; i < lines.Length; i++) {
|
||||
string line = lines[i];
|
||||
|
||||
if (line.StartsWith(header) && !string.IsNullOrEmpty(chunk)) {
|
||||
chunks.Add(chunk.Trim());
|
||||
chunk = string.Empty;
|
||||
}
|
||||
|
||||
chunk = chunk + "\n" + line;
|
||||
}
|
||||
|
||||
int curchunk = 0;
|
||||
string botheader = BotHeader().Trim() + "\n";
|
||||
// Leave room for botheader and some leeway
|
||||
int maxlength = (2000 - botheader.Length - 10);
|
||||
|
||||
// Then try to collate chunks
|
||||
for (curchunk = 0; curchunk < chunks.Count; ++curchunk) {
|
||||
int count = chunks[curchunk].Length;
|
||||
while (count < maxlength && (curchunk+1) < chunks.Count) {
|
||||
count += chunks[curchunk + 1].Length + 2;
|
||||
if (count < maxlength) {
|
||||
chunks[curchunk] += "\n";
|
||||
chunks[curchunk] += chunks[curchunk + 1];
|
||||
chunks.RemoveAt(curchunk + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Readd bott headers
|
||||
for (int i = 0; i < chunks.Count; i++) {
|
||||
chunks[i] = chunks[i].Insert(0, botheader);
|
||||
}
|
||||
|
||||
// Now finally check if any one chunk is bigger than max length
|
||||
for (int i = 0; i < chunks.Count; i++) {
|
||||
if (chunks[i].Length > maxcount) {
|
||||
throw new ApplicationException(
|
||||
string.Format("a chunk turned out bigger than {0}", maxcount));
|
||||
}
|
||||
}
|
||||
|
||||
return chunks.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
32
EliteBGS/DiscordPoster.cs
Normal file
32
EliteBGS/DiscordPoster.cs
Normal 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, string username = "EDBGS") {
|
||||
JsonObject obj = new();
|
||||
obj.Add("content", log);
|
||||
obj.Add("username", username);
|
||||
|
||||
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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0-windows</TargetFramework>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<Version>0.4.2</Version>
|
||||
<Version>0.4.3</Version>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
<UseWPF>true</UseWPF>
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
namespace EliteBGS;
|
||||
using System.Collections.Generic;
|
||||
using System.Windows.Documents;
|
||||
|
||||
namespace EliteBGS;
|
||||
|
||||
public class GenericDiscordLog : DiscordLogGenerator {
|
||||
public override string ToString() {
|
||||
@@ -8,4 +11,8 @@ public class GenericDiscordLog : DiscordLogGenerator {
|
||||
public override string Name {
|
||||
get { return "Generic"; }
|
||||
}
|
||||
|
||||
public override string[] SplitLog(string log, int maxcount = 2000) {
|
||||
return SplitLogWithHeader(log, "**Date:**", maxcount);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ public partial class LoadEntriesWindow : Window {
|
||||
dialog.DefaultExt = ".log";
|
||||
dialog.Filter = "Log files (*.log)|*.log|All files (*.*)|*";
|
||||
|
||||
var location = config.Global.DefaultJournalLocation;
|
||||
var location = AppConfig.DefaultJournalLocation;
|
||||
if (Directory.Exists(location)) {
|
||||
dialog.InitialDirectory = location;
|
||||
}
|
||||
|
||||
@@ -64,6 +64,7 @@ public class MissionFormat : LogFormatter {
|
||||
Dictionary<string, ulong> passengers = new();
|
||||
StringBuilder output = new StringBuilder();
|
||||
long total_influence = 0;
|
||||
bool hadmissions = false;
|
||||
|
||||
var missions = objective.EnabledOfType<MissionCompleted>();
|
||||
var support = objective.EnabledOfType<InfluenceSupport>();
|
||||
@@ -85,6 +86,7 @@ public class MissionFormat : LogFormatter {
|
||||
|
||||
++collated[m.MissionName][m.Influence];
|
||||
|
||||
hadmissions = true;
|
||||
total_influence += m.Influence.Length;
|
||||
|
||||
if (m.AcceptedEntry != null &&
|
||||
@@ -121,19 +123,23 @@ public class MissionFormat : LogFormatter {
|
||||
output.Append(failedlog);
|
||||
output.Append("\n");
|
||||
}
|
||||
if (failed.Count > 0) {
|
||||
hadmissions = true;
|
||||
}
|
||||
total_influence += failed.Sum(x => x.InfluenceAmount);
|
||||
|
||||
foreach (InfluenceSupport inf in support) {
|
||||
output.Append(inf.ToString());
|
||||
output.Append("\n");
|
||||
hadmissions = true;
|
||||
total_influence += inf.Influence.InfluenceAmount;
|
||||
}
|
||||
|
||||
if (support.Count() > 0) {
|
||||
if (support.Count > 0) {
|
||||
output.Append("\n");
|
||||
}
|
||||
|
||||
if (total_influence != 0) {
|
||||
if (hadmissions) {
|
||||
output.AppendFormat("Total Influence: {0}", total_influence);
|
||||
}
|
||||
|
||||
@@ -141,6 +147,10 @@ public class MissionFormat : LogFormatter {
|
||||
}
|
||||
|
||||
public string GenerateSummary(Objective objective) {
|
||||
bool hadmissions = objective
|
||||
.EnabledOfType<MissionCompleted>()
|
||||
.Count > 0
|
||||
;
|
||||
long influence = objective
|
||||
.EnabledOfType<MissionCompleted>()
|
||||
.Sum(x => x.Influence.Length)
|
||||
@@ -154,7 +164,7 @@ public class MissionFormat : LogFormatter {
|
||||
.Sum(x => x.InfluenceAmount)
|
||||
;
|
||||
|
||||
if (influence == 0 && support == 0 && failed == 0) {
|
||||
if (influence == 0 && support == 0 && failed == 0 && !hadmissions) {
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
@@ -5,9 +5,13 @@
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
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"
|
||||
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>
|
||||
<local:MinusFortyFiveConverter x:Key="MinusFortyFiveConverter" />
|
||||
<Style x:Key="StretchingTreeViewStyle" TargetType="TreeViewItem" BasedOn="{StaticResource {x:Type TreeViewItem}}">
|
||||
@@ -15,6 +19,23 @@
|
||||
</Style>
|
||||
|
||||
<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}">
|
||||
<Grid
|
||||
@@ -100,6 +121,7 @@
|
||||
<Button Content="Ground" x:Name="Ground" Click="Ground_Click"/>
|
||||
<Button Content="Ship" x:Name="Ship" Click="Ship_Click"/>
|
||||
<Button Content="AX" x:Name="Thargoid" Click="Thargoid_Click"/>
|
||||
<Button Content="Power" x:Name="Power" Click="Power_Click"/>
|
||||
</StackPanel>
|
||||
</Expander>
|
||||
</StackPanel>
|
||||
@@ -162,6 +184,11 @@
|
||||
<ComboBox x:Name="LogType" VerticalAlignment="Stretch" Margin="0,3,0,3" Width="140" SelectionChanged="LogType_SelectionChanged" />
|
||||
<Separator />
|
||||
<CheckBox x:Name="SelectAll" Content="Select All" IsChecked="True" Click="SelectAll_Click"/>
|
||||
<Separator />
|
||||
<Label Content="Post log as" VerticalAlignment="Center" Margin="0,3,0,3" />
|
||||
<ComboBox x:Name="Commanders" VerticalAlignment="Stretch" Margin="0,3,0,3" Width="180" />
|
||||
<Label Content="to" VerticalAlignment="Center" Margin="0,3,0,3" />
|
||||
<mah:DropDownButton x:Name="PostToDiscord" Content="Discord" />
|
||||
</ToolBar>
|
||||
<TreeView CheckBox.Checked="TreeView_CheckBox_Updated"
|
||||
CheckBox.Unchecked="TreeView_CheckBox_Updated"
|
||||
@@ -189,6 +216,7 @@
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
@@ -244,6 +272,35 @@
|
||||
<mah:ToggleSwitch x:Name="NoFleetCarrier" Grid.Row="2" Grid.ColumnSpan="2" Content="Ignore transactions done on a Fleet Carrier" Toggled="NoFleetCarrier_Toggled" />
|
||||
</Grid>
|
||||
</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>
|
||||
</TabItem>
|
||||
<TabItem Header="Event Log">
|
||||
|
||||
@@ -46,6 +46,9 @@ public partial class MainWindow : MetroWindow {
|
||||
/* ignored */
|
||||
}
|
||||
|
||||
Webhooks.ItemsSource = Config.Global.Webhooks;
|
||||
RefreshPostMenu();
|
||||
|
||||
foreach (DiscordLogGenerator type in logtypes) {
|
||||
LogType.Items.Add(type);
|
||||
}
|
||||
@@ -182,6 +185,11 @@ public partial class MainWindow : MetroWindow {
|
||||
|
||||
List<Transaction> transactions = parser.Parse(entries, options);
|
||||
|
||||
Commanders.ItemsSource = parser.Commanders;
|
||||
if (Commanders.Items.Count > 0) {
|
||||
Commanders.SelectedIndex = 0;
|
||||
}
|
||||
|
||||
// Filter the transactions down to the given time frame
|
||||
transactions = transactions
|
||||
.Where(t => t.CompletedAtDateTime >= start && t.CompletedAtDateTime <= end)
|
||||
@@ -468,6 +476,16 @@ public partial class MainWindow : MetroWindow {
|
||||
RefreshView();
|
||||
}
|
||||
|
||||
private void Power_Click(object sender, RoutedEventArgs e) {
|
||||
CombatZone transaction = GetTransaction<CombatZone>(sender);
|
||||
if (transaction == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
transaction.Type = CombatZones.PowerCombatZone;
|
||||
RefreshView();
|
||||
}
|
||||
|
||||
private void Thargoid_Click(object sender, RoutedEventArgs e) {
|
||||
CombatZone transaction = GetTransaction<CombatZone>(sender);
|
||||
if (transaction == null) {
|
||||
@@ -580,4 +598,130 @@ public partial class MainWindow : MetroWindow {
|
||||
}
|
||||
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 = "All";
|
||||
menu.Click += PostToAll_Click;
|
||||
PostToDiscord.Items.Add(menu);
|
||||
}
|
||||
}
|
||||
|
||||
private void PostToDiscordWebhook(IEnumerable<DiscordWebhook> hooks) {
|
||||
if (string.IsNullOrEmpty(DiscordLog.Text)) {
|
||||
return;
|
||||
}
|
||||
|
||||
DiscordLogGenerator discord = LogType.SelectedItem as DiscordLogGenerator;
|
||||
string[] chunks;
|
||||
|
||||
try {
|
||||
chunks = discord.SplitLog(DiscordLog.Text);
|
||||
} catch (Exception) {
|
||||
MessageBox.Show(
|
||||
"The log could not be split into discord appropriate length.\n" +
|
||||
"This happens with the bigger logs formats if you do lots of things in one system.\n" +
|
||||
"Try posting the log in the OneLine format.",
|
||||
"Sorry!", MessageBoxButton.OK, MessageBoxImage.Error
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
string commander = "EDBGS";
|
||||
if (Commanders.SelectedIndex >= 0) {
|
||||
commander = Commanders.SelectedItem.ToString();
|
||||
}
|
||||
|
||||
foreach (var hook in hooks) {
|
||||
try {
|
||||
foreach (var chunk in chunks) {
|
||||
DiscordPoster.PostToDiscord(hook, chunk, commander);
|
||||
}
|
||||
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;
|
||||
}
|
||||
PostToDiscordWebhook(new DiscordWebhook[] { hook });
|
||||
}
|
||||
|
||||
private void PostToAll_Click(object sender, RoutedEventArgs e) {
|
||||
PostToDiscordWebhook(Config.Global.Webhooks);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -91,4 +91,8 @@ public class NonaDiscordLog : DiscordLogGenerator {
|
||||
public override string Name {
|
||||
get { return "NovaNavy"; }
|
||||
}
|
||||
|
||||
public override string[] SplitLog(string log, int maxcount = 2000) {
|
||||
return SplitLogWithHeader(log, ":clock2: `Date:`", maxcount);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +59,9 @@ public class UITransaction : INotifyPropertyChanged {
|
||||
return Visibility.Hidden;
|
||||
}
|
||||
|
||||
if (string.Compare(combat.Type, CombatZones.ShipCombatZone) == 0) {
|
||||
// Both normal and power combat zones have special objectives
|
||||
if (string.Compare(combat.Type, CombatZones.ShipCombatZone) == 0 ||
|
||||
string.Compare(combat.Type, CombatZones.PowerCombatZone) == 0) {
|
||||
return Visibility.Visible;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
using EliteBGS.LogGenerator;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Windows.Documents;
|
||||
|
||||
namespace EliteBGS;
|
||||
|
||||
@@ -25,6 +28,30 @@ public class OneLineDiscordLog : DiscordLogGenerator {
|
||||
return "";
|
||||
}
|
||||
|
||||
public override string[] SplitLog(string log, int maxcount = 2000) {
|
||||
string[] lines = log.Split('\n');
|
||||
List<string> chunks = new();
|
||||
string chunk = string.Empty;
|
||||
|
||||
// Optimisation
|
||||
if (log.Length <= maxcount) {
|
||||
return new string[] { log };
|
||||
}
|
||||
|
||||
for (int i = 0; i < lines.Length; i++) {
|
||||
string line = lines[i];
|
||||
if ((chunk.Length + line.Length) > maxcount || i == lines.Length - 1) {
|
||||
chunks.Add(chunk.Trim());
|
||||
chunk = string.Empty;
|
||||
chunk = chunk.Insert(0, BotHeader()).Trim();
|
||||
} else {
|
||||
chunk = chunk + "\n" + line;
|
||||
}
|
||||
}
|
||||
|
||||
return chunks.ToArray();
|
||||
}
|
||||
|
||||
public override string GenerateDiscordLog(Report report) {
|
||||
StringBuilder log = new StringBuilder();
|
||||
|
||||
|
||||
@@ -49,5 +49,5 @@ using System.Windows;
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("0.4.2.0")]
|
||||
[assembly: AssemblyFileVersion("0.4.2.0")]
|
||||
[assembly: AssemblyVersion("0.4.3.0")]
|
||||
[assembly: AssemblyFileVersion("0.4.3.0")]
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
using Newtonsoft.Json;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace EliteBGS.Util {
|
||||
public class AppConfig {
|
||||
namespace EliteBGS.Util;
|
||||
|
||||
public class AppConfig {
|
||||
private static readonly string default_journal_location = "%UserProfile%\\Saved Games\\Frontier Developments\\Elite Dangerous";
|
||||
private string journal_location = default_journal_location;
|
||||
public string DefaultJournalLocation => default_journal_location;
|
||||
private string colour = "Amber";
|
||||
private string theme = "Dark";
|
||||
|
||||
public static string DefaultJournalLocation => default_journal_location;
|
||||
|
||||
public string LastUsedDiscordTemplate { get; set; }
|
||||
|
||||
public string JournalLocation {
|
||||
@@ -63,9 +67,13 @@ namespace EliteBGS.Util {
|
||||
/// </summary>
|
||||
public bool IgnoreFleetCarrier { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// List of Webhooks configured
|
||||
/// </summary>
|
||||
public List<DiscordWebhook> Webhooks { get; set; } = new List<DiscordWebhook>();
|
||||
|
||||
[JsonIgnore]
|
||||
public string FullTheme {
|
||||
get { return Theme + "." + Colour; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Text;
|
||||
using System.IO;
|
||||
using Newtonsoft.Json;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace EliteBGS.Util {
|
||||
public class Config {
|
||||
|
||||
13
EliteBGS/Util/DiscordWebhook.cs
Normal file
13
EliteBGS/Util/DiscordWebhook.cs
Normal 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;
|
||||
}
|
||||
Reference in New Issue
Block a user