Compare commits
	
		
			42 Commits
		
	
	
		
			5ea288ee86
			...
			6cbe54ff73
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 6cbe54ff73 | |||
| 6fd5bbc582 | |||
| 262182cfaf | |||
| 007b391dc2 | |||
| 9918c7d559 | |||
| 2bf8d9018d | |||
| 8eaf94f634 | |||
| 20adf93d39 | |||
| fd3e5f61cb | |||
| e617c3852b | |||
| 9f013bed38 | |||
| 916afb2348 | |||
| 6eb892151c | |||
| 6a9e4978aa | |||
| 0203008202 | |||
| 03621721b8 | |||
| ccba55ac35 | |||
| 0708880284 | |||
| 18c3073635 | |||
| c43d2ff1d3 | |||
| a3b7623557 | |||
| 1c2fc1e2e6 | |||
| 53da6b4bc2 | |||
| bc44ceb205 | |||
| 4d3048a37d | |||
| 463598c779 | |||
| bf56f3a2d5 | |||
| 450824733d | |||
| 110f909c6f | |||
| a1099628a0 | |||
| 7e85159fd5 | |||
| 2e8daca61c | |||
| 05b714a607 | |||
| 3b0492c70f | |||
| c20280eb13 | |||
| dd863c326e | |||
| 2012aab7b3 | |||
| abb7954614 | |||
| 0f44a6a9a7 | |||
| 34e0a0c8ba | |||
| b0b82811cc | |||
| 4855d78823 | 
| @ -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; | ||||
|         } | ||||
|  | ||||
							
								
								
									
										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(); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										84
									
								
								EDPlayerJournal/BGS/Parsers/RedeemVoucherParser.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								EDPlayerJournal/BGS/Parsers/RedeemVoucherParser.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,84 @@ | ||||
| using EDPlayerJournal.Entries; | ||||
| 
 | ||||
| namespace EDPlayerJournal.BGS.Parsers; | ||||
| 
 | ||||
| internal class RedeemVoucherParser : ITransactionParserPart { | ||||
|     public void Parse(Entry e, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) { | ||||
|         RedeemVoucherEntry? entry = e as RedeemVoucherEntry; | ||||
|         if (entry == null) { | ||||
|             throw new NotImplementedException(); | ||||
|         } | ||||
| 
 | ||||
|         if (context.CurrentSystem == null) { | ||||
|             transactions.AddIncomplete(new Vouchers(), | ||||
|                 "Could not find out where the vouchers were redeemed", e | ||||
|                 ); | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         List<Faction>? current_factions = context.GetFactions(context.CurrentSystem); | ||||
|         if (current_factions == null) { | ||||
|             transactions.AddIncomplete(new Vouchers(), | ||||
|                 "Current system factions are unknown, so vouchers were ineffective", e); | ||||
|         } | ||||
| 
 | ||||
|         foreach (string faction in entry.Factions) { | ||||
|             bool relevantBond = false; | ||||
|             string relevantFaction = faction; | ||||
| 
 | ||||
|             if (string.Compare(faction, Factions.PilotsFederationVouchers) == 0) { | ||||
|                 // Target faction is pilots' federation, so we assume thargoid bonks | ||||
|                 // Also assign this combat bond to the Pilots Federation | ||||
|                 relevantFaction = Factions.PilotsFederation; | ||||
|                 relevantBond = true; | ||||
|             } | ||||
| 
 | ||||
|             if (current_factions != null && !relevantBond) { | ||||
|                 // If we have local factions, and it ain't thargoid bonds see if the bonds were | ||||
|                 // useful in the current system | ||||
|                 if (current_factions.Find(x => string.Compare(x.Name, faction, true) == 0) != null) { | ||||
|                     relevantBond = true; | ||||
|                 } else { | ||||
|                     transactions.AddIncomplete(new Vouchers(), | ||||
|                         string.Format("Vouchers for \"{0}\" had no effect in \"{1}\" since said " + | ||||
|                         "faction is not present there", faction, context.CurrentSystem), e | ||||
|                     ); | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             if (!relevantBond) { | ||||
|                 continue; | ||||
|             } | ||||
| 
 | ||||
|             var voucher = new Vouchers(entry) { | ||||
|                 System = context.CurrentSystem, | ||||
|                 Station = context.CurrentStation, | ||||
|                 Faction = relevantFaction, | ||||
|                 ControllingFaction = context.ControllingFaction, | ||||
|                 IsLegacy = context.IsLegacy, | ||||
|             }; | ||||
| 
 | ||||
|             if (options.FilterDoubleRedeemVouchers) { | ||||
|                 // To filter out doubly redeemed vouchers, find another redeem voucher | ||||
|                 // event that happened in the same system, same total sum, and also the | ||||
|                 // same faction. If there is one, filter this one out. | ||||
|                 var doubledEntry = transactions | ||||
|                     .OfType<Vouchers>() | ||||
|                     .Where(x => x.TotalSum == voucher.TotalSum && | ||||
|                                 x.System == voucher.System && | ||||
|                                 x.Faction == voucher.Faction) | ||||
|                     .ToList() | ||||
|                     ; | ||||
|                 if (doubledEntry.Count > 0) { | ||||
|                     transactions.AddIncomplete( | ||||
|                         voucher,  | ||||
|                         string.Format("A doubled redeem voucher for {0} valued {1} was detected", | ||||
|                                       voucher.Faction, Credits.FormatMillions(voucher.TotalSum)),  | ||||
|                         e); | ||||
|                     return; | ||||
|                 } | ||||
|             } | ||||
|             transactions.Add(voucher); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -49,10 +49,11 @@ public class SellCargo : Transaction { | ||||
|         } | ||||
| 
 | ||||
|         foreach (MarketSellEntry sell in sold) { | ||||
|             builder.AppendFormat("Sold {0} {1} to the {2}", | ||||
|             builder.AppendFormat("Sold {0} {1} to the {2} of {3}", | ||||
|                 sell.Count, | ||||
|                 Cargo, | ||||
|                 Market | ||||
|                 Market, | ||||
|                 Station | ||||
|                 ); | ||||
| 
 | ||||
|             if (Profit != 0) { | ||||
|  | ||||
| @ -28,6 +28,14 @@ public class TransactionParserOptions { | ||||
|     /// Whether we should ignore things done for the fleet carrier faction. | ||||
|     /// </summary> | ||||
|     public bool IgnoreFleetCarrierFaction { get; set; } = true; | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Filter out double redeem vouchers that happen when you redeem a specific | ||||
|     /// voucher, and then redeem the rest of your vouchers (say from a KWS) in | ||||
|     /// bulk. The bulk redeem will also list the first voucher redeem again in | ||||
|     /// its bulk list. | ||||
|     /// </summary> | ||||
|     public bool FilterDoubleRedeemVouchers { get; set; } = true; | ||||
| } | ||||
| 
 | ||||
| public class TransactionList : List<Transaction> { | ||||
| @ -422,63 +430,6 @@ internal class MissionFailedParser : ITransactionParserPart { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| internal class RedeemVoucherParser : ITransactionParserPart { | ||||
|     public void Parse(Entry e, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) { | ||||
|         RedeemVoucherEntry? entry = e as RedeemVoucherEntry; | ||||
|         if (entry == null) { | ||||
|             throw new NotImplementedException(); | ||||
|         } | ||||
| 
 | ||||
|         if (context.CurrentSystem == null) { | ||||
|             transactions.AddIncomplete(new Vouchers(), | ||||
|                 "Could not find out where the vouchers were redeemed", e | ||||
|                 ); | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         List<Faction>? current_factions = context.GetFactions(context.CurrentSystem); | ||||
|         if (current_factions == null) { | ||||
|             transactions.AddIncomplete(new Vouchers(), | ||||
|                 "Current system factions are unknown, so vouchers were ineffective", e); | ||||
|         } | ||||
| 
 | ||||
|         foreach (string faction in entry.Factions) { | ||||
|             bool relevantBond = false; | ||||
|             string relevantFaction = faction; | ||||
| 
 | ||||
|             if (string.Compare(faction, Factions.PilotsFederationVouchers) == 0) { | ||||
|                 // Target faction is pilots' federation, so we assume thargoid bonks | ||||
|                 // Also assign this combat bond to the Pilots Federation | ||||
|                 relevantFaction = Factions.PilotsFederation; | ||||
|                 relevantBond = true; | ||||
|             } | ||||
| 
 | ||||
|             if (current_factions != null && !relevantBond) { | ||||
|                 // If we have local factions, and it ain't thargoid bonds see if the bonds were | ||||
|                 // useful in the current system | ||||
|                 if (current_factions.Find(x => string.Compare(x.Name, faction, true) == 0) != null) { | ||||
|                     relevantBond = true; | ||||
|                 } else { | ||||
|                     transactions.AddIncomplete(new Vouchers(), | ||||
|                         string.Format("Vouchers for \"{0}\" had no effect in \"{1}\" since said " + | ||||
|                         "faction is not present there", faction, context.CurrentSystem), e | ||||
|                     ); | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             if (relevantBond) { | ||||
|                 transactions.Add(new Vouchers(entry) { | ||||
|                     System = context.CurrentSystem, | ||||
|                     Station = context.CurrentStation, | ||||
|                     Faction = relevantFaction, | ||||
|                     ControllingFaction = context.ControllingFaction, | ||||
|                     IsLegacy = context.IsLegacy, | ||||
|                 }); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| internal class SellMicroResourcesParser : ITransactionParserPart { | ||||
|     public void Parse(Entry e, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) { | ||||
|         SellMicroResourcesEntry? entry = e as SellMicroResourcesEntry; | ||||
| @ -651,15 +602,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() | ||||
|     { | ||||
| @ -718,6 +660,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(); | ||||
| @ -735,6 +682,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> | ||||
|  | ||||
| @ -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; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -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; | ||||
| } | ||||
|  | ||||
| @ -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>(); | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										7
									
								
								EDPlayerJournalTests/double-vouchers-2.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								EDPlayerJournalTests/double-vouchers-2.txt
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,7 @@ | ||||
| {"timestamp":"2024-04-27T13:27:08Z","event":"Fileheader","part":1,"language":"English/UK","Odyssey":true,"gameversion":"4.0.0.1803","build":"r301470/r0 "} | ||||
| {"timestamp":"2024-04-27T13:27:34Z","event":"Commander","FID":"F9183790","Name":"Jeremaya"} | ||||
| {"timestamp":"2024-04-27T15:06:31Z","event":"FSDJump","Taxi":false,"Multicrew":false,"StarSystem":"HIP 3318","SystemAddress":525890177387,"StarPos":[50.21875,-190.6875,37.5],"SystemAllegiance":"Empire","SystemEconomy":"$economy_Refinery;","SystemEconomy_Localised":"Refinery","SystemSecondEconomy":"$economy_Extraction;","SystemSecondEconomy_Localised":"Extraction","SystemGovernment":"$government_Patronage;","SystemGovernment_Localised":"Patronage","SystemSecurity":"$SYSTEM_SECURITY_high;","SystemSecurity_Localised":"High Security","Population":239484,"Body":"HIP 3318 A","BodyID":2,"BodyType":"Star","JumpDist":27.495,"FuelUsed":3.115176,"FuelLevel":13.228082,"Factions":[{"Name":"HIP 3318 Autocracy","FactionState":"None","Government":"Dictatorship","Influence":0.119192,"Allegiance":"Empire","Happiness":"$Faction_HappinessBand2;","Happiness_Localised":"Happy","MyReputation":0.0},{"Name":"Chakho Gold Galactic Limited","FactionState":"None","Government":"Corporate","Influence":0.09798,"Allegiance":"Empire","Happiness":"$Faction_HappinessBand2;","Happiness_Localised":"Happy","MyReputation":0.0},{"Name":"HIP 3318 Interstellar","FactionState":"None","Government":"Corporate","Influence":0.075758,"Allegiance":"Empire","Happiness":"$Faction_HappinessBand2;","Happiness_Localised":"Happy","MyReputation":0.0},{"Name":"HIP 3318 Values Party","FactionState":"None","Government":"Democracy","Influence":0.032323,"Allegiance":"Independent","Happiness":"$Faction_HappinessBand2;","Happiness_Localised":"Happy","MyReputation":0.0},{"Name":"Nationalists of HIP 3318","FactionState":"None","Government":"Dictatorship","Influence":0.035354,"Allegiance":"Empire","Happiness":"$Faction_HappinessBand2;","Happiness_Localised":"Happy","MyReputation":0.0},{"Name":"Nova Paresa","FactionState":"Boom","Government":"Patronage","Influence":0.434343,"Allegiance":"Empire","Happiness":"$Faction_HappinessBand2;","Happiness_Localised":"Happy","SquadronFaction":true,"MyReputation":100.0,"PendingStates":[{"State":"Expansion","Trend":0}]},{"Name":"Empire Consulate Ltd","FactionState":"None","Government":"Patronage","Influence":0.20505,"Allegiance":"Empire","Happiness":"$Faction_HappinessBand2;","Happiness_Localised":"Happy","MyReputation":94.474998}],"SystemFaction":{"Name":"Nova Paresa","FactionState":"Boom"}} | ||||
| {"timestamp":"2024-04-27T15:12:23Z","event":"ApproachSettlement","Name":"Koh Biological Installation","MarketID":3803792128,"StationFaction":{"Name":"Nova Paresa","FactionState":"Boom"},"StationGovernment":"$government_Patronage;","StationGovernment_Localised":"Patronage","StationAllegiance":"Empire","StationServices":["dock","autodock","commodities","contacts","exploration","missions","refuel","repair","engineer","missionsgenerated","flightcontroller","stationoperations","searchrescue","stationMenu"],"StationEconomy":"$economy_HighTech;","StationEconomy_Localised":"High Tech","StationEconomies":[{"Name":"$economy_HighTech;","Name_Localised":"High Tech","Proportion":1.0}],"SystemAddress":525890177387,"BodyID":26,"BodyName":"HIP 3318 D 3","Latitude":-0.893061,"Longitude":-62.928345} | ||||
| {"timestamp":"2024-04-27T15:14:08Z","event":"Docked","StationName":"Koh Biological Installation","StationType":"OnFootSettlement","Taxi":false,"Multicrew":false,"StarSystem":"HIP 3318","SystemAddress":525890177387,"MarketID":3803792128,"StationFaction":{"Name":"Nova Paresa","FactionState":"Boom"},"StationGovernment":"$government_Patronage;","StationGovernment_Localised":"Patronage","StationAllegiance":"Empire","StationServices":["dock","autodock","commodities","contacts","exploration","missions","refuel","repair","engineer","missionsgenerated","flightcontroller","stationoperations","searchrescue","stationMenu"],"StationEconomy":"$economy_HighTech;","StationEconomy_Localised":"High Tech","StationEconomies":[{"Name":"$economy_HighTech;","Name_Localised":"High Tech","Proportion":1.0}],"DistFromStarLS":11972.63409,"LandingPads":{"Small":1,"Medium":0,"Large":1}} | ||||
| {"timestamp":"2024-04-27T15:15:10Z","event":"RedeemVoucher","Type":"bounty","Amount":9449329,"Factions":[{"Faction":"Nova Paresa","Amount":9449329}]} | ||||
| {"timestamp":"2024-04-27T15:15:51Z","event":"RedeemVoucher","Type":"bounty","Amount":18780130,"Factions":[{"Faction":"","Amount":224449},{"Faction":"","Amount":730880},{"Faction":"","Amount":1272764},{"Faction":"","Amount":580384},{"Faction":"","Amount":10180261},{"Faction":"","Amount":1413153},{"Faction":"","Amount":1365934},{"Faction":"","Amount":202193},{"Faction":"Nova Paresa","Amount":9449329},{"Faction":"","Amount":104412},{"Faction":"","Amount":2453443},{"Faction":"","Amount":252257}]} | ||||
| @ -1,12 +1,47 @@ | ||||
| # EliteBGS changelog | ||||
| 
 | ||||
| # 0.3.7 on 29.01.2024 | ||||
| ## 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 | ||||
|   well as the name of of the log format used. This makes it easier for bots | ||||
|   to parse these logs. Since the different formats have become popular, its | ||||
|   always good to make it easier for bots to parse the logs. | ||||
| 
 | ||||
| ## 0.4.1 on 28.04.2024 | ||||
| 
 | ||||
| * Filter out vouchers that are redeemed twice, due to bulk turn-in. If you | ||||
|   redeem a singular voucher for value X, and then redeem the rest of your | ||||
|   vouchers - say KWS vouchers - in bulk, the first voucher of value X will | ||||
|   appear again in the logs. It appears twice in the logs, but only counts | ||||
|   once. | ||||
| * Add the market name to the trading log entries. | ||||
| 
 | ||||
| ## 0.4.0 on 13.04.2024 | ||||
| 
 | ||||
| * Change layout of the results into System -> Faction -> Transaction. | ||||
|   Many people who contribute *a lot* of things to the BGS have preferred such | ||||
|   a layout to find the right things to post to various discord guilds/threads. | ||||
| * Sort all systems in the new overview by name. It just makes them easier to | ||||
|   find when there are a lot of entries. | ||||
| * Add a button to deselect/select all buttons. | ||||
| 
 | ||||
| ## 0.3.7 on 29.01.2024 | ||||
| 
 | ||||
| * Fix wrong locations of BGS action if you remain on your carrier while its | ||||
|   jumping to a new system. | ||||
| * Identify a capital ship in a high CZ by its music. | ||||
| 
 | ||||
| # 0.3.6 on 25.10.2023 | ||||
| ## 0.3.6 on 25.10.2023 | ||||
| 
 | ||||
| * U17 introduced invalid JSON into the player journal. EliteBGS can now skip over | ||||
|   those and keep processing. This way players won't have to delete lines in their | ||||
| @ -111,10 +111,6 @@ public class DiscordLogGenerator { | ||||
| 
 | ||||
|         string summary = GenerateSummary(objective); | ||||
| 
 | ||||
|         log.AppendFormat("**Log Generated:** {0} by {1}\n",  | ||||
|             DateTime.Now.ToString("dd/MM/yyyy"), | ||||
|             GetToolVersion() | ||||
|             ); | ||||
|         var earliest = GetDateOfEarliestEntry(objective); | ||||
|         var latest = GetDateOfLatestEntry(objective); | ||||
|         if (earliest != null && latest != null) { | ||||
| @ -125,7 +121,7 @@ public class DiscordLogGenerator { | ||||
|         } | ||||
|         log.AppendFormat("**Target:** {0}\n", location); | ||||
|         if (!string.IsNullOrEmpty(summary)) { | ||||
|             log.AppendFormat("**Summary**: {0}\n", summary); | ||||
|             log.AppendFormat("**Summary:** {0}\n", summary); | ||||
|         } | ||||
|         if (legacycount > 0) { | ||||
|             log.AppendFormat("**Warning:** Some actions were performed on ED Legacy\n"); | ||||
| @ -149,6 +145,16 @@ public class DiscordLogGenerator { | ||||
|         return log; | ||||
|     } | ||||
| 
 | ||||
|     public virtual string Name { | ||||
|         get { return "GenericLog"; } | ||||
|     } | ||||
| 
 | ||||
|     protected virtual string BotHeader() { | ||||
|         var sb = new StringBuilder(); | ||||
|         sb.AppendFormat("**Bot-Header:** {0}; {1}\n", GetToolVersion(), this.Name); | ||||
|         return sb.ToString(); | ||||
|     } | ||||
| 
 | ||||
|     public virtual string GenerateDiscordLog(Report report) { | ||||
|         StringBuilder log = new StringBuilder(); | ||||
| 
 | ||||
| @ -164,6 +170,7 @@ public class DiscordLogGenerator { | ||||
|             return ""; | ||||
|         } | ||||
| 
 | ||||
|         log.AppendFormat("{0}", BotHeader()); | ||||
|         log.AppendFormat("{0}", GenerateHeader()); | ||||
| 
 | ||||
|         foreach (Objective objective in objectives) { | ||||
| @ -191,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.3.7</Version> | ||||
|     <Version>0.4.3</Version> | ||||
|     <GenerateAssemblyInfo>false</GenerateAssemblyInfo> | ||||
|     <UseWindowsForms>true</UseWindowsForms> | ||||
|     <UseWPF>true</UseWPF> | ||||
| @ -18,12 +18,15 @@ | ||||
|     <Copyright>Copyright 2019 by Florian Stinglmayr</Copyright> | ||||
|     <RepositoryUrl>https://codeberg.org/nola/EDBGS</RepositoryUrl> | ||||
|     <PackageTags>ED;Elite Dangerous;BGS</PackageTags> | ||||
|     <PackageProjectUrl>https://bgs.n0la.org</PackageProjectUrl> | ||||
|     <PackageProjectUrl>https://salusinvicta.org/bgstool/</PackageProjectUrl> | ||||
|     <PackageReadmeFile>README.md</PackageReadmeFile> | ||||
|     <Description>Elite: Dangerous BGS logging and reporting tool | ||||
| </Description> | ||||
|     <PackageIcon>logo_v5.png</PackageIcon> | ||||
|   </PropertyGroup> | ||||
|   <ItemGroup> | ||||
|     <None Remove="main-page.png" /> | ||||
|   </ItemGroup> | ||||
|   <ItemGroup> | ||||
|     <Resource Include="main-page.png"> | ||||
|       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> | ||||
| @ -37,15 +40,10 @@ | ||||
|       <Pack>True</Pack> | ||||
|       <PackagePath>\</PackagePath> | ||||
|     </None> | ||||
|     <None Update="docs\CHANGELOG.md"> | ||||
|     <None Update="CHANGELOG.md"> | ||||
|       <CopyToOutputDirectory>Always</CopyToOutputDirectory> | ||||
|     </None> | ||||
|   </ItemGroup> | ||||
|   <ItemGroup> | ||||
|     <Resource Include="docs\main-page.png"> | ||||
|       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> | ||||
|     </Resource> | ||||
|   </ItemGroup> | ||||
|   <ItemGroup> | ||||
|     <Content Include="LICENCE.txt"> | ||||
|       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> | ||||
| @ -55,12 +53,12 @@ | ||||
|     <Resource Include="Salus.ico" /> | ||||
|   </ItemGroup> | ||||
|   <ItemGroup> | ||||
|     <PackageReference Include="MahApps.Metro" Version="2.4.9" /> | ||||
|     <PackageReference Include="MahApps.Metro" Version="2.4.10" /> | ||||
|     <PackageReference Include="Microsoft.CSharp" Version="4.7.0" /> | ||||
|     <PackageReference Include="Microsoft.Windows.Compatibility" Version="7.0.0" /> | ||||
|     <PackageReference Include="Microsoft.Windows.Compatibility" Version="8.0.4" /> | ||||
|   </ItemGroup> | ||||
|   <ItemGroup> | ||||
|     <PackageReference Include="Newtonsoft.Json" Version="13.0.2" /> | ||||
|     <PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> | ||||
|     <PackageReference Include="Ookii.Dialogs.Wpf" Version="5.0.1" /> | ||||
|   </ItemGroup> | ||||
|   <ItemGroup> | ||||
|  | ||||
| @ -1,7 +1,18 @@ | ||||
| namespace EliteBGS; | ||||
| using System.Collections.Generic; | ||||
| using System.Windows.Documents; | ||||
| 
 | ||||
| namespace EliteBGS; | ||||
| 
 | ||||
| public class GenericDiscordLog : DiscordLogGenerator { | ||||
|     public override string ToString() { | ||||
|         return "Generic Log"; | ||||
|         return "Generic"; | ||||
|     } | ||||
| 
 | ||||
|     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; | ||||
|         } | ||||
|  | ||||
| @ -5,75 +5,57 @@ | ||||
|         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}}"> | ||||
|             <Setter Property="HorizontalContentAlignment" Value="Stretch"/> | ||||
|         </Style> | ||||
|         <local:MinusFortyFiveConverter x:Key="MinusFortyFiveConverter" /> | ||||
|     </Window.Resources> | ||||
|     <mah:MetroWindow.RightWindowCommands> | ||||
|         <mah:WindowCommands ShowSeparators="False"> | ||||
|             <TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Margin="5,0,5,0"> | ||||
|                 <Hyperlink x:Name="URL" NavigateUri="https://bgs.n0la.org/" RequestNavigate="URL_RequestNavigate">Homepage</Hyperlink> | ||||
|             </TextBlock> | ||||
|             <TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Margin="5,0,15,0"> | ||||
|                 <Hyperlink x:Name="SRC" NavigateUri="https://codeberg.org/nola/EDBGS" RequestNavigate="URL_RequestNavigate">Source</Hyperlink> | ||||
|             </TextBlock> | ||||
|             <mah:ToggleSwitch Content="Dark Theme" x:Name="SwitchTheme" IsOn="True" Toggled="SwitchTheme_Toggled"/> | ||||
|         </mah:WindowCommands> | ||||
|     </mah:MetroWindow.RightWindowCommands> | ||||
| 
 | ||||
|         <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> | ||||
|         <Grid.RowDefinitions> | ||||
|             <RowDefinition Height="*"/> | ||||
|         </Grid.RowDefinitions> | ||||
|         <TabControl Style="{DynamicResource MahApps.Styles.TabControl.Animated}"> | ||||
|             <TabItem Header="Current Objectives"> | ||||
|                 <Grid> | ||||
|                     <Grid.RowDefinitions> | ||||
|                         <RowDefinition Height="Auto"/> | ||||
|                         <RowDefinition Height="Auto"/> | ||||
|                         <RowDefinition/> | ||||
|                         <RowDefinition Height="Auto"/> | ||||
|                         <RowDefinition/> | ||||
|                     </Grid.RowDefinitions> | ||||
|                     <Grid.ColumnDefinitions> | ||||
|                         <ColumnDefinition Width="*"/> | ||||
|                         <ColumnDefinition Width="*"/> | ||||
|                         <ColumnDefinition Width="*"/> | ||||
|                     </Grid.ColumnDefinitions> | ||||
|                     <ToolBar VerticalAlignment="Top" Grid.Row="0" Width="Auto" Margin="0,0,0,0" Height="Auto" Grid.ColumnSpan="3" HorizontalAlignment="Left"> | ||||
|                         <Button x:Name="ParseJournal" Content="Parse Journal" VerticalAlignment="Center" Click="ParseJournal_Click" HorizontalAlignment="Center"/> | ||||
|                         <Separator Margin="1" VerticalAlignment="Center" MinWidth="1" HorizontalAlignment="Center" MinHeight="22"/> | ||||
|                         <Label Content="From (UTC):" VerticalAlignment="Center" VerticalContentAlignment="Center" HorizontalAlignment="Center"/> | ||||
|                         <mah:DateTimePicker x:Name="startdate" Height="26.2857142857143" VerticalAlignment="Center" HorizontalAlignment="Center"/> | ||||
|                         <Label Content="To (UTC):" Height="26.2857142857143" VerticalAlignment="Center" HorizontalAlignment="Center"/> | ||||
|                         <mah:DateTimePicker x:Name="enddate" Height="26.2857142857143" VerticalAlignment="Center" HorizontalAlignment="Center"/> | ||||
|                         <Separator Margin="1" VerticalAlignment="Center" MinWidth="1" HorizontalAlignment="Center" MinHeight="22"/> | ||||
|                         <Button x:Name="ResetTime" Content="Reset Time" Click="ResetTime_Click" /> | ||||
|                         <Separator Margin="1" VerticalAlignment="Center" MinWidth="1" HorizontalAlignment="Center" MinHeight="22"/> | ||||
|                         <Button x:Name="ManuallyParse" Content="Manually Parse" Click="ManuallyParse_Click" /> | ||||
|                     </ToolBar> | ||||
|                     <ToolBar Grid.Row="1" HorizontalAlignment="Left" Height="Auto" VerticalAlignment="Top" Width="Auto" Grid.ColumnSpan="3"> | ||||
|                         <Button x:Name="GenerateDiscord" Content="Generate Discord Report" VerticalAlignment="Stretch" Margin="0,0,0,0" VerticalContentAlignment="Center" Click="GenerateDiscord_Click"/> | ||||
|                         <Separator /> | ||||
|                         <ComboBox x:Name="LogType" VerticalAlignment="Stretch" Margin="0,3,0,3" Width="140" SelectionChanged="LogType_SelectionChanged" /> | ||||
|                     </ToolBar> | ||||
|                     <TreeView CheckBox.Checked="TreeView_CheckBox_Updated" | ||||
|                               CheckBox.Unchecked="TreeView_CheckBox_Updated" | ||||
|                               x:Name="entries" Margin="0,0,0,0" | ||||
|                               Grid.ColumnSpan="3" Grid.Row="2" | ||||
|                               KeyUp="entries_KeyUp" | ||||
|                 <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 | ||||
|                  HorizontalAlignment="Stretch" | ||||
|                               HorizontalContentAlignment="Stretch" | ||||
|                  Width="{Binding ActualWidth, ElementName=entries, Converter={StaticResource MinusFortyFiveConverter}}" | ||||
|                 > | ||||
|                         <TreeView.ItemTemplate> | ||||
|                             <HierarchicalDataTemplate DataType="{x:Type local:Objective}" ItemsSource="{Binding UITransactions}" ItemContainerStyle="{StaticResource StretchingTreeViewStyle}"> | ||||
|                 <Grid.ColumnDefinitions> | ||||
|                     <ColumnDefinition Width="Auto"/> | ||||
|                 </Grid.ColumnDefinitions> | ||||
|                 <StackPanel Grid.Column="0" Orientation="Horizontal" VerticalAlignment="Center" Margin="0,2,0,2"> | ||||
|                     <CheckBox Focusable="False" IsChecked="{Binding IsEnabled}" VerticalAlignment="Center"/> | ||||
|                     <TextBlock Text="System: " Margin="2,0,0,0"/> | ||||
|                     <TextBlock Text="{Binding SystemName}" FontWeight="DemiBold"/> | ||||
|                 </StackPanel> | ||||
|             </Grid> | ||||
|         </HierarchicalDataTemplate> | ||||
| 
 | ||||
|         <HierarchicalDataTemplate DataType="{x:Type local:Objective}" | ||||
|                                   ItemsSource="{Binding Path=UITransactions}" | ||||
|                                   ItemContainerStyle="{StaticResource StretchingTreeViewStyle}"> | ||||
|             <Grid | ||||
|                                      HorizontalAlignment="Stretch" | ||||
|                                      Width="{Binding ActualWidth, ElementName=entries, Converter={StaticResource MinusFortyFiveConverter}}" | ||||
| @ -85,10 +67,8 @@ | ||||
|                 </Grid.ColumnDefinitions> | ||||
|                 <StackPanel Grid.Column="0" Orientation="Horizontal" VerticalAlignment="Center" Margin="0,2,0,2"> | ||||
|                     <CheckBox Focusable="False" IsChecked="{Binding IsEnabled}" VerticalAlignment="Center"/> | ||||
|                                         <TextBlock Text="System: " Visibility="{Binding HasSystem}" Margin="2,0,0,0"/> | ||||
|                                         <TextBlock Text="{Binding System}" FontWeight="DemiBold" Visibility="{Binding HasSystem}"/> | ||||
|                                         <TextBlock Text="Faction: " Visibility="{Binding HasFaction}" Margin="2,0,0,0"/> | ||||
|                                         <TextBlock Text="{Binding Faction}" FontWeight="DemiBold" Visibility="{Binding HasFaction}"/> | ||||
|                     <TextBlock Text="Faction: " Margin="2,0,0,0"/> | ||||
|                     <TextBlock Text="{Binding Faction}" FontWeight="DemiBold"/> | ||||
|                 </StackPanel> | ||||
|                 <Separator Visibility="Hidden" Grid.Column="1" HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch" /> | ||||
|                 <StackPanel Grid.Column="2" HorizontalAlignment="Right" VerticalAlignment="Center" Orientation="Horizontal"> | ||||
| @ -97,8 +77,9 @@ | ||||
|                     <Button x:Name="AddCombatZone" Content="Add Combat Zone" Click="AddCombatZone_Click" /> | ||||
|                 </StackPanel> | ||||
|             </Grid> | ||||
|                                 <HierarchicalDataTemplate.ItemTemplate> | ||||
|                                     <HierarchicalDataTemplate> | ||||
|         </HierarchicalDataTemplate> | ||||
| 
 | ||||
|         <DataTemplate DataType="{x:Type local:UITransaction}"> | ||||
|             <!-- This will stretch out the width of the item--> | ||||
|             <Grid Initialized="Transaction_Initialized" | ||||
|                                             HorizontalAlignment="Left" | ||||
| @ -148,10 +129,76 @@ | ||||
|                     <TextBox x:Name="Profit" MinWidth="80" HorizontalContentAlignment="Right" Text="{Binding Profit, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" LostFocus="Profit_LostFocus" KeyUp="Profit_KeyUp"/> | ||||
|                 </StackPanel> | ||||
|             </Grid> | ||||
|                                     </HierarchicalDataTemplate> | ||||
|                                 </HierarchicalDataTemplate.ItemTemplate> | ||||
|                             </HierarchicalDataTemplate> | ||||
|                         </TreeView.ItemTemplate> | ||||
|         </DataTemplate> | ||||
| 
 | ||||
|     </Window.Resources> | ||||
|     <mah:MetroWindow.RightWindowCommands> | ||||
|         <mah:WindowCommands ShowSeparators="False"> | ||||
|             <TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Margin="5,0,5,0"> | ||||
|                 <Hyperlink x:Name="URL" NavigateUri="https://salusinvicta.org/bgstool/" RequestNavigate="URL_RequestNavigate">Homepage</Hyperlink> | ||||
|             </TextBlock> | ||||
|             <TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Margin="5,0,15,0"> | ||||
|                 <Hyperlink x:Name="SRC" NavigateUri="https://codeberg.org/nola/EDBGS" RequestNavigate="URL_RequestNavigate">Source</Hyperlink> | ||||
|             </TextBlock> | ||||
|             <mah:ToggleSwitch Content="Dark Theme" x:Name="SwitchTheme" IsOn="True" Toggled="SwitchTheme_Toggled"/> | ||||
|         </mah:WindowCommands> | ||||
|     </mah:MetroWindow.RightWindowCommands> | ||||
|     <Grid> | ||||
|         <Grid.ColumnDefinitions> | ||||
|             <ColumnDefinition Width="*"/> | ||||
|         </Grid.ColumnDefinitions> | ||||
|         <Grid.RowDefinitions> | ||||
|             <RowDefinition Height="*"/> | ||||
|         </Grid.RowDefinitions> | ||||
|         <TabControl Style="{DynamicResource MahApps.Styles.TabControl.Animated}"> | ||||
|             <TabItem Header="Current Objectives"> | ||||
|                 <Grid> | ||||
|                     <Grid.RowDefinitions> | ||||
|                         <RowDefinition Height="Auto"/> | ||||
|                         <RowDefinition Height="Auto"/> | ||||
|                         <RowDefinition/> | ||||
|                         <RowDefinition Height="Auto"/> | ||||
|                         <RowDefinition/> | ||||
|                     </Grid.RowDefinitions> | ||||
|                     <Grid.ColumnDefinitions> | ||||
|                         <ColumnDefinition Width="*"/> | ||||
|                         <ColumnDefinition Width="*"/> | ||||
|                         <ColumnDefinition Width="*"/> | ||||
|                     </Grid.ColumnDefinitions> | ||||
|                     <ToolBar VerticalAlignment="Top" Grid.Row="0" Width="Auto" Margin="0,0,0,0" Height="Auto" Grid.ColumnSpan="3" HorizontalAlignment="Left"> | ||||
|                         <Button x:Name="ParseJournal" Content="Parse Journal" VerticalAlignment="Center" Click="ParseJournal_Click" HorizontalAlignment="Center"/> | ||||
|                         <Separator Margin="1" VerticalAlignment="Center" MinWidth="1" HorizontalAlignment="Center" MinHeight="22"/> | ||||
|                         <Label Content="From (UTC):" VerticalAlignment="Center" VerticalContentAlignment="Center" HorizontalAlignment="Center"/> | ||||
|                         <mah:DateTimePicker x:Name="startdate" Height="26.2857142857143" VerticalAlignment="Center" HorizontalAlignment="Center"/> | ||||
|                         <Label Content="To (UTC):" Height="26.2857142857143" VerticalAlignment="Center" HorizontalAlignment="Center"/> | ||||
|                         <mah:DateTimePicker x:Name="enddate" Height="26.2857142857143" VerticalAlignment="Center" HorizontalAlignment="Center"/> | ||||
|                         <Separator Margin="1" VerticalAlignment="Center" MinWidth="1" HorizontalAlignment="Center" MinHeight="22"/> | ||||
|                         <Button x:Name="ResetTime" Content="Reset Time" Click="ResetTime_Click" /> | ||||
|                         <Separator Margin="1" VerticalAlignment="Center" MinWidth="1" HorizontalAlignment="Center" MinHeight="22"/> | ||||
|                         <Button x:Name="ManuallyParse" Content="Manually Parse" Click="ManuallyParse_Click" /> | ||||
|                     </ToolBar> | ||||
|                     <ToolBar Grid.Row="1" HorizontalAlignment="Left" Height="Auto" VerticalAlignment="Top" Width="Auto" Grid.ColumnSpan="3"> | ||||
|                         <Button x:Name="GenerateDiscord" Content="Generate Discord Report" VerticalAlignment="Stretch" Margin="0,0,0,0" VerticalContentAlignment="Center" Click="GenerateDiscord_Click"/> | ||||
|                         <Separator /> | ||||
|                         <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" | ||||
|                               x:Name="entries" Margin="0,0,0,0" | ||||
|                               Grid.ColumnSpan="3" | ||||
|                               Grid.Row="2" | ||||
|                               KeyUp="entries_KeyUp" | ||||
|                               HorizontalAlignment="Stretch" | ||||
|                               HorizontalContentAlignment="Stretch" | ||||
|                               ItemsSource="{Binding Source={StaticResource ObjectivesBasedOnSystem}}" | ||||
|                               > | ||||
|                         <TreeView.ItemContainerStyle> | ||||
|                             <Style TargetType="TreeViewItem"> | ||||
|                                 <Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" /> | ||||
| @ -168,6 +215,7 @@ | ||||
|                         <RowDefinition Height="Auto"/> | ||||
|                         <RowDefinition Height="Auto"/> | ||||
|                         <RowDefinition Height="Auto"/> | ||||
|                         <RowDefinition Height="Auto"/> | ||||
|                         <RowDefinition Height="*"/> | ||||
|                     </Grid.RowDefinitions> | ||||
|                     <Grid.ColumnDefinitions> | ||||
| @ -223,6 +271,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"> | ||||
|  | ||||
| @ -32,8 +32,8 @@ public partial class MainWindow : MetroWindow { | ||||
|     private LoadEntriesWindow loadentries = null; | ||||
| 
 | ||||
|     private static readonly List<DiscordLogGenerator> logtypes = new List<DiscordLogGenerator>() { | ||||
|         new NonaDiscordLog(), | ||||
|         new GenericDiscordLog(), | ||||
|         new NonaDiscordLog(), | ||||
|         new OneLineDiscordLog(), | ||||
|     }; | ||||
| 
 | ||||
| @ -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); | ||||
|         } | ||||
| @ -107,6 +110,10 @@ public partial class MainWindow : MetroWindow { | ||||
|             //{ "HouseSalus", Color.FromRgb(0xBC, 0x94, 0x39) }, | ||||
|             { "HouseSalus", Color.FromRgb(0xED, 0xDA, 0x70) }, | ||||
|             { "NovaNavy", Color.FromRgb(0xA1, 0xA4, 0xDB) }, | ||||
|             // Official Red of the Polish Flag | ||||
|             { "PolskaGurom", Color.FromRgb(0xD4, 0x21, 0x3D) }, | ||||
|             // Official Blue in the Armenian Flag | ||||
|             { "ArmeniaBlue", Color.FromRgb(0x00, 0x33, 0xA0) }, | ||||
|         }; | ||||
| 
 | ||||
|         foreach (var colourtheme in colorThemes) { | ||||
| @ -178,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) | ||||
| @ -192,7 +204,7 @@ public partial class MainWindow : MetroWindow { | ||||
|             transactions.RemoveAll(x => incompletes.Contains(x)); | ||||
| 
 | ||||
|             report = new Report(transactions); | ||||
|             this.entries.ItemsSource = report.Objectives; | ||||
|             this.entries.ItemsSource = report.SystemObjectives; | ||||
|         } catch (Exception exception) { | ||||
|             Log("Something went terribly wrong while parsing the E:D player journal."); | ||||
|             Log("Please send this to CMDR Hekateh:"); | ||||
| @ -271,15 +283,27 @@ public partial class MainWindow : MetroWindow { | ||||
|         object obj = entries.SelectedItem; | ||||
|         bool removed = false; | ||||
| 
 | ||||
|         if (obj.GetType() == typeof(Objective)) { | ||||
|             removed = report.Objectives.Remove(obj as Objective); | ||||
|         } else if (obj.GetType() == typeof(UITransaction) || | ||||
|             obj.GetType().IsSubclassOf(typeof(UITransaction))) { | ||||
|             foreach (Objective parent in report.Objectives) { | ||||
|                 if (parent.UITransactions.Remove(obj as UITransaction)) { | ||||
|         if (obj.GetType() == typeof(SystemObjectives)) { | ||||
|             removed = report.SystemObjectives.Remove(obj as SystemObjectives); | ||||
|         } else if (obj.GetType() == typeof(Objective)) { | ||||
|             report | ||||
|                 .SystemObjectives | ||||
|                 .ForEach(x => { | ||||
|                     if (x.Objectives.Remove(obj as Objective)) { | ||||
|                         removed = true; | ||||
|                     } | ||||
|             } | ||||
|                 }); | ||||
|         } else if (obj.GetType() == typeof(UITransaction) || | ||||
|                    obj.GetType().IsSubclassOf(typeof(UITransaction))) { | ||||
|             report | ||||
|                 .SystemObjectives | ||||
|                 .SelectMany(x =>  | ||||
|                     x.Objectives | ||||
|                     .Where(x => x.UITransactions.Contains(obj as UITransaction)) | ||||
|                     ) | ||||
|                 .ToList() | ||||
|                 .ForEach(x => removed = x.UITransactions.Remove(obj as UITransaction)) | ||||
|                 ; | ||||
|         } | ||||
| 
 | ||||
|         if (removed) { | ||||
| @ -557,4 +581,136 @@ public partial class MainWindow : MetroWindow { | ||||
|         } catch (Exception) { | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private void SelectAll_Click(object sender, RoutedEventArgs e) { | ||||
|         if (report == null) { | ||||
|             return; | ||||
|         } | ||||
|         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); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -8,7 +8,7 @@ public class MinusFortyFiveConverter : IValueConverter { | ||||
|     /// <inheritdoc/> | ||||
|     public object Convert( | ||||
|         object value, Type targetType, object parameter, CultureInfo culture) { | ||||
|         return (double)value - 80; | ||||
|         return (double)value - 110; | ||||
|     } | ||||
| 
 | ||||
|     /// <inheritdoc/> | ||||
|  | ||||
| @ -7,6 +7,12 @@ using System.Linq; | ||||
| namespace EliteBGS.BGS; | ||||
| 
 | ||||
| public class NonaDiscordLog : DiscordLogGenerator { | ||||
|     protected override string BotHeader() { | ||||
|         var sb = new StringBuilder(); | ||||
|         sb.AppendFormat(":robot: `Bot-Header:` {0}; {1}\n", GetToolVersion(), this.Name); | ||||
|         return sb.ToString(); | ||||
|     } | ||||
| 
 | ||||
|     private string FormatDate() { | ||||
|         CultureInfo cultureInfo = CultureInfo.InvariantCulture; | ||||
|         StringBuilder date = new StringBuilder(); | ||||
| @ -79,6 +85,14 @@ public class NonaDiscordLog : DiscordLogGenerator { | ||||
|     } | ||||
| 
 | ||||
|     public override string ToString() { | ||||
|         return "Nova Navy Log"; | ||||
|         return "Nova Navy"; | ||||
|     } | ||||
| 
 | ||||
|     public override string Name { | ||||
|         get { return "NovaNavy"; } | ||||
|     } | ||||
| 
 | ||||
|     public override string[] SplitLog(string log, int maxcount = 2000) { | ||||
|         return SplitLogWithHeader(log, ":clock2: `Date:`", maxcount); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -242,7 +242,7 @@ public class UITransaction : INotifyPropertyChanged { | ||||
| } | ||||
| 
 | ||||
| public class Objective : IComparable<Objective> { | ||||
|     public bool IsEnabled { get; set; } | ||||
|     public bool IsEnabled { get; set; } = true; | ||||
| 
 | ||||
|     public List<UITransaction> UITransactions { get; } = new List<UITransaction>(); | ||||
| 
 | ||||
|  | ||||
| @ -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(); | ||||
| 
 | ||||
| @ -40,6 +67,8 @@ public class OneLineDiscordLog : DiscordLogGenerator { | ||||
|             return ""; | ||||
|         } | ||||
| 
 | ||||
|         log.AppendFormat("{0}", BotHeader()); | ||||
| 
 | ||||
|         foreach (Objective objective in objectives) { | ||||
|             log.AppendFormat("{0}", GenerateObjectiveHeader(objective)); | ||||
| 
 | ||||
| @ -60,6 +89,10 @@ public class OneLineDiscordLog : DiscordLogGenerator { | ||||
|     } | ||||
| 
 | ||||
|     public override string ToString() { | ||||
|         return "One Line Report"; | ||||
|         return "One Line"; | ||||
|     } | ||||
| 
 | ||||
|     public override string Name { | ||||
|         get { return "OneLine"; } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -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.3.7.0")] | ||||
| [assembly: AssemblyFileVersion("0.3.7.0")] | ||||
| [assembly: AssemblyVersion("0.4.3.0")] | ||||
| [assembly: AssemblyFileVersion("0.4.3.0")] | ||||
|  | ||||
| @ -7,7 +7,7 @@ been parsed, you may then generate a BGS report you can copy/paste into Discord. | ||||
| 
 | ||||
| Source code is available at [https://codeberg.org/nola/edbgs](https://codeberg.org/nola/edbgs). | ||||
| 
 | ||||
| Binary downloads can be found here: [https://bgs.n0la.org/](https://bgs.n0la.org/). | ||||
| Binary downloads can be found here: [https://salusinvicta.org/bgstool/](https://salusinvicta.org/bgstool/). | ||||
| 
 | ||||
| ## How To | ||||
| 
 | ||||
|  | ||||
| @ -1,10 +1,38 @@ | ||||
| using System.Collections.Generic; | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.ComponentModel; | ||||
| using System.Linq; | ||||
| using EDPlayerJournal.BGS; | ||||
| 
 | ||||
| namespace EliteBGS; | ||||
| 
 | ||||
| public class SystemObjectives : INotifyPropertyChanged, IComparable<SystemObjectives> { | ||||
|     public event PropertyChangedEventHandler PropertyChanged; | ||||
| 
 | ||||
|     private bool isenabled = true; | ||||
| 
 | ||||
|     public bool IsEnabled { | ||||
|         get { return isenabled; } | ||||
|         set { | ||||
|             isenabled = value; | ||||
|             PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("IsEnabled")); | ||||
|         }  | ||||
|     } | ||||
| 
 | ||||
|     public bool IsExpanded { get; set; } = false; | ||||
| 
 | ||||
|     public string SystemName { get; set; } = string.Empty; | ||||
| 
 | ||||
|     public List<Objective> Objectives { get; set; } = new(); | ||||
| 
 | ||||
|     public int CompareTo(SystemObjectives other) { | ||||
|         if (other == null) return 1; | ||||
|         return string.Compare(SystemName, other.SystemName, StringComparison.OrdinalIgnoreCase); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| public class Report { | ||||
|     public List<Objective> Objectives { get; set; } = new List<Objective>(); | ||||
|     public List<SystemObjectives> SystemObjectives { get; set; } = new(); | ||||
| 
 | ||||
|     public Report() { } | ||||
| 
 | ||||
| @ -12,29 +40,35 @@ public class Report { | ||||
|         Populate(transactions); | ||||
|     } | ||||
| 
 | ||||
|     public List<Objective> Objectives { | ||||
|         get { | ||||
|             return SystemObjectives | ||||
|                 .Where(t => t.IsEnabled) | ||||
|                 .SelectMany(x => x.Objectives) | ||||
|                 .ToList() | ||||
|                 ; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private void Populate(List<Transaction> transactions) { | ||||
|         if (transactions == null || transactions.Count == 0) { | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         foreach (Transaction t in transactions) { | ||||
|             Objective o; | ||||
|             if (t.SystemContribution) { | ||||
|                 o = Objectives.Find(x => x.Matches(t.System)); | ||||
|             } else { | ||||
|                 o = Objectives.Find(x => x.Matches(t.System, t.Faction)); | ||||
|             } | ||||
| 
 | ||||
|             var o = SystemObjectives.Find(x => string.Compare(x.SystemName, t.System) == 0); | ||||
|             if (o == null) { | ||||
|                 if (t.SystemContribution) { | ||||
|                     o = new Objective() { System = t.System }; | ||||
|                 } else { | ||||
|                     o = new Objective() { Faction = t.Faction, System = t.System }; | ||||
|                 o = new SystemObjectives() { SystemName = t.System }; | ||||
|                 SystemObjectives.Add(o); | ||||
|             } | ||||
|                 Objectives.Add(o); | ||||
|             var objective = o.Objectives.Find(x => x.Matches(t.System, t.Faction)); | ||||
|             if (objective == null) { | ||||
|                 objective = new Objective() { Faction = t.Faction, System = t.System }; | ||||
|                 o.Objectives.Add(objective); | ||||
|             } | ||||
|             objective.UITransactions.Add(new UITransaction(t)); | ||||
|         } | ||||
| 
 | ||||
|             o.UITransactions.Add(new UITransaction(t)); | ||||
|         } | ||||
|         SystemObjectives.Sort(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,13 +1,17 @@ | ||||
| using Newtonsoft.Json; | ||||
| using System.Collections.Generic; | ||||
| using System.ComponentModel; | ||||
| 
 | ||||
| namespace EliteBGS.Util; | ||||
| 
 | ||||
| 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; | ||||
| } | ||||
							
								
								
									
										
											BIN
										
									
								
								EliteBGS/combatzone.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								EliteBGS/combatzone.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 75 KiB | 
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 68 KiB | 
| @ -26,7 +26,7 @@ You can download the **latest** version **0.3.7** at CodeBerg: | ||||
| 
 | ||||
| Or alternatively from my server: | ||||
| 
 | ||||
| * [https://bgs.n0la.org/elitebgs-0.3.6.zip](https://bgs.n0la.org/elitebgs-0.3.7.zip) | ||||
| * [https://bgs.n0la.org/elitebgs-0.3.7.zip](https://bgs.n0la.org/elitebgs-0.3.7.zip) | ||||
| 
 | ||||
| ## Old Versions | ||||
| 
 | ||||
|  | ||||
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 67 KiB | 
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 67 KiB After Width: | Height: | Size: 74 KiB | 
| @ -1,15 +0,0 @@ | ||||
| site_name: EliteBGS | ||||
| 
 | ||||
| markdown_extensions: | ||||
|     - pymdownx.snippets: | ||||
|           check_paths: true | ||||
| 
 | ||||
| theme: | ||||
|     name: lumen | ||||
| 
 | ||||
| nav: | ||||
|     - Overview: 'index.md' | ||||
|     - "Detailed Description": 'description.md' | ||||
|     - "Combat Zones": 'combatzones.md' | ||||
|     - FAQ: 'faq.md' | ||||
|     - Changelog: 'CHANGELOG.md' | ||||
| @ -3,7 +3,7 @@ | ||||
| EDBGS is a project containing the EliteBGS BGS application. It also contains the dotnet | ||||
| class library EDPlayerJournal, which reads and parses Elite Dangerous player journals. | ||||
| 
 | ||||
| See [https://bgs.n0la.org/](https://bgs.n0la.org) for further details. | ||||
| See [https://salusinvicta.org/bgstool/](https://salusinvicta.org/bgstool/) for further details. | ||||
| 
 | ||||
| The tool helps with creating BGS reports for squadrons that support factions in | ||||
| Elite: Dangerous that can be copy and pasted into a Discord server. It finds all BGS | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user