Skip to content

Commit

Permalink
Limit status report delay
Browse files Browse the repository at this point in the history
  • Loading branch information
Citrinate committed Apr 28, 2024
1 parent cff1939 commit 000f351
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 25 deletions.
16 changes: 8 additions & 8 deletions BoosterManager/Commands.cs
Original file line number Diff line number Diff line change
Expand Up @@ -347,18 +347,18 @@ internal static class Commands {
return await ResponseSendItemToBot(access, steamID, Utilities.GetArgsAsText(args, 1, ","), ItemIdentifier.SackIdentifier).ConfigureAwait(false);

case "MARKET2FAOK" or "M2FAOK" when args.Length > 2:
return await Response2FAOK(access, steamID, args[1], Confirmation.EConfirmationType.Market, args[2], new StatusReporter(bot, steamID, reportDelaySeconds: 30)).ConfigureAwait(false);
return await Response2FAOK(access, steamID, args[1], Confirmation.EConfirmationType.Market, args[2], new StatusReporter(bot, steamID, reportDelaySeconds: 30, reportMaxDelaySeconds: 60)).ConfigureAwait(false);
case "MARKET2FAOK" or "M2FAOK":
return await Response2FAOK(access, steamID, args[1], Confirmation.EConfirmationType.Market).ConfigureAwait(false);
case "MARKET2FAOKA" or "M2FAOKA":
return await Response2FAOK(access, steamID, "ASF", Confirmation.EConfirmationType.Market, args[1], new StatusReporter(bot, steamID, reportDelaySeconds: 30)).ConfigureAwait(false);
return await Response2FAOK(access, steamID, "ASF", Confirmation.EConfirmationType.Market, args[1], new StatusReporter(bot, steamID, reportDelaySeconds: 30, reportMaxDelaySeconds: 60)).ConfigureAwait(false);

case "TRADE2FAOK" or "T2FAOK" when args.Length > 2:
return await Response2FAOK(access, steamID, args[1], Confirmation.EConfirmationType.Trade, args[2], new StatusReporter(bot, steamID, reportDelaySeconds: 30)).ConfigureAwait(false);
return await Response2FAOK(access, steamID, args[1], Confirmation.EConfirmationType.Trade, args[2], new StatusReporter(bot, steamID, reportDelaySeconds: 30, reportMaxDelaySeconds: 60)).ConfigureAwait(false);
case "TRADE2FAOK" or "T2FAOK":
return await Response2FAOK(access, steamID, args[1], Confirmation.EConfirmationType.Trade).ConfigureAwait(false);
case "TRADE2FAOKA" or "T2FAOKA":
return await Response2FAOK(access, steamID, "ASF", Confirmation.EConfirmationType.Trade, args[1], new StatusReporter(bot, steamID, reportDelaySeconds: 30)).ConfigureAwait(false);
return await Response2FAOK(access, steamID, "ASF", Confirmation.EConfirmationType.Trade, args[1], new StatusReporter(bot, steamID, reportDelaySeconds: 30, reportMaxDelaySeconds: 60)).ConfigureAwait(false);

case "TRADECHECK" or "TCHECK" or "TC":
return ResponseTradeCheck(access, steamID, args[1]);
Expand Down Expand Up @@ -515,19 +515,19 @@ internal static class Commands {
}

if (minutes == 0) {
if ((acceptedType == Confirmation.EConfirmationType.Market && MarketHandler.StopMarketRepeatTimer(bot))
|| (acceptedType == Confirmation.EConfirmationType.Trade && InventoryHandler.StopTradeRepeatTimer(bot))
if ((acceptedType == Confirmation.EConfirmationType.Market && await MarketHandler.StopMarketRepeatTimer(bot).ConfigureAwait(false))
|| (acceptedType == Confirmation.EConfirmationType.Trade && await InventoryHandler.StopTradeRepeatTimer(bot).ConfigureAwait(false))
) {
return FormatBotResponse(bot, Strings.RepetitionCancelled);
} else {
return FormatBotResponse(bot, Strings.RepetitionNotActive);
}
} else {
if (acceptedType == Confirmation.EConfirmationType.Market) {
MarketHandler.StartMarketRepeatTimer(bot, minutes, statusReporter);
await MarketHandler.StartMarketRepeatTimer(bot, minutes, statusReporter).ConfigureAwait(false);
repeatMessage = String.Format(Strings.RepetitionNotice, minutes, String.Format("!m2faok {0} 0", bot.BotName));
} else if (acceptedType == Confirmation.EConfirmationType.Trade) {
InventoryHandler.StartTradeRepeatTimer(bot, minutes, statusReporter);
await InventoryHandler.StartTradeRepeatTimer(bot, minutes, statusReporter).ConfigureAwait(false);
repeatMessage = String.Format(Strings.RepetitionNotice, minutes, String.Format("!t2faok {0} 0", bot.BotName));
}
}
Expand Down
18 changes: 12 additions & 6 deletions BoosterManager/Handlers/InventoryHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

namespace BoosterManager {
internal static class InventoryHandler {
private static ConcurrentDictionary<Bot, Timer> TradeRepeatTimers = new();
private static ConcurrentDictionary<Bot, (Timer, StatusReporter?)> TradeRepeatTimers = new();

internal static async Task<string> SendItemToMultipleBots(Bot sender, List<(Bot reciever, uint amount)> recievers, uint appID, ulong contextID, ItemIdentifier itemIdentifier) {
// Send Amounts A,B,... of Item X to Bots C,D,... from Bot E
Expand Down Expand Up @@ -232,26 +232,32 @@ internal static async Task<string> GetItemCount(Bot bot, uint appID, ulong conte
return Commands.FormatBotResponse(bot, response);
}

internal static bool StopTradeRepeatTimer(Bot bot) {
internal static async Task<bool> StopTradeRepeatTimer(Bot bot) {
if (!TradeRepeatTimers.ContainsKey(bot)) {
return false;
}

if (TradeRepeatTimers.TryRemove(bot, out Timer? oldTimer)) {
if (TradeRepeatTimers.TryRemove(bot, out (Timer, StatusReporter?) item)) {
(Timer? oldTimer, StatusReporter? statusReporter) = item;

if (oldTimer != null) {
oldTimer.Change(Timeout.Infinite, Timeout.Infinite);
oldTimer.Dispose();
}

if (statusReporter != null) {
await statusReporter.Send().ConfigureAwait(false);
}
}

return true;
}

internal static void StartTradeRepeatTimer(Bot bot, uint minutes, StatusReporter? statusReporter) {
StopTradeRepeatTimer(bot);
internal static async Task StartTradeRepeatTimer(Bot bot, uint minutes, StatusReporter? statusReporter) {
await StopTradeRepeatTimer(bot).ConfigureAwait(false);

Timer newTimer = new Timer(async _ => await InventoryHandler.AcceptTradeConfirmations(bot, statusReporter).ConfigureAwait(false), null, Timeout.Infinite, Timeout.Infinite);
if (TradeRepeatTimers.TryAdd(bot, newTimer)) {
if (TradeRepeatTimers.TryAdd(bot, (newTimer, statusReporter))) {
newTimer.Change(TimeSpan.FromMinutes(minutes), TimeSpan.FromMinutes(minutes));
} else {
newTimer.Dispose();
Expand Down
18 changes: 12 additions & 6 deletions BoosterManager/Handlers/MarketHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

namespace BoosterManager {
internal static class MarketHandler {
private static ConcurrentDictionary<Bot, Timer> MarketRepeatTimers = new();
private static ConcurrentDictionary<Bot, (Timer, StatusReporter?)> MarketRepeatTimers = new();

internal static async Task<string> GetListings(Bot bot) {
uint? listingsValue = await GetMarketListingsValue(bot).ConfigureAwait(false);
Expand Down Expand Up @@ -240,26 +240,32 @@ internal static async Task<string> FindAndRemoveListings(Bot bot, List<ItemIdent
return filteredListings;
}

internal static bool StopMarketRepeatTimer(Bot bot) {
internal static async Task<bool> StopMarketRepeatTimer(Bot bot) {
if (!MarketRepeatTimers.ContainsKey(bot)) {
return false;
}

if (MarketRepeatTimers.TryRemove(bot, out Timer? oldTimer)) {
if (MarketRepeatTimers.TryRemove(bot, out (Timer, StatusReporter?) item)) {
(Timer? oldTimer, StatusReporter? statusReporter) = item;

if (oldTimer != null) {
oldTimer.Change(Timeout.Infinite, Timeout.Infinite);
oldTimer.Dispose();
}

if (statusReporter != null) {
await statusReporter.Send().ConfigureAwait(false);
}
}

return true;
}

internal static void StartMarketRepeatTimer(Bot bot, uint minutes, StatusReporter? statusReporter) {
StopMarketRepeatTimer(bot);
internal static async Task StartMarketRepeatTimer(Bot bot, uint minutes, StatusReporter? statusReporter) {
await StopMarketRepeatTimer(bot).ConfigureAwait(false);

Timer newTimer = new Timer(async _ => await MarketHandler.AcceptMarketConfirmations(bot, statusReporter).ConfigureAwait(false), null, Timeout.Infinite, Timeout.Infinite);
if (MarketRepeatTimers.TryAdd(bot, newTimer)) {
if (MarketRepeatTimers.TryAdd(bot, (newTimer, statusReporter))) {
newTimer.Change(TimeSpan.FromMinutes(minutes), TimeSpan.FromMinutes(minutes));
} else {
newTimer.Dispose();
Expand Down
33 changes: 28 additions & 5 deletions BoosterManager/Handlers/StatusReporter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
using SteamKit2;

// For when long-running commands are issued through Steam chat, this is used to send status reports from the bot the command was sent to, to the user who issued the command
// If the commands weren't issued through Steam chat, just log the status reports
// If the commands weren't issued through Steam chat, this just logs the status reports

namespace BoosterManager {
internal sealed class StatusReporter {
Expand All @@ -25,15 +25,18 @@ internal sealed class StatusReporter {
private ConcurrentDictionary<Bot, List<string>> Reports = new();
private ConcurrentDictionary<Bot, List<string>> PreviousReports = new();
private uint ReportDelaySeconds;
private uint ReportMaxDelaySeconds;
private const uint DefaultReportDelaySeconds = 5;

private Timer? ReportTimer;
private DateTime? ReportMaxDelayTime = null;
private SemaphoreSlim ReportSemaphore = new SemaphoreSlim(1, 1);

internal StatusReporter(Bot? sender = null, ulong recipientSteamID = 0, uint reportDelaySeconds = DefaultReportDelaySeconds) {
internal StatusReporter(Bot? sender = null, ulong recipientSteamID = 0, uint reportDelaySeconds = DefaultReportDelaySeconds, uint? reportMaxDelaySeconds = null) {
SenderSteamID = sender?.SteamID ?? 0;
RecipientSteamID = recipientSteamID;
ReportDelaySeconds = reportDelaySeconds;
ReportMaxDelaySeconds = reportMaxDelaySeconds ?? reportDelaySeconds * 5;
}

[JsonConstructor]
Expand Down Expand Up @@ -68,22 +71,38 @@ internal void Report(Bot reportingBot, string report, bool suppressDuplicateMess
Reports.AddOrUpdate(reportingBot, new List<string>() { report }, (_, reports) => { reports.Add(report); return reports; });

// I prefer to send all reports in as few messages as possible
// As long as reports continue to come in, we wait
// As long as reports continue to come in, we wait (until some limit, to avoid possibly waiting forever)

double delayCorrectionSeconds = 0;
if (ReportMaxDelayTime != null) {
if (ReportMaxDelayTime <= DateTime.Now) {
return;
}

delayCorrectionSeconds = Math.Max(0, (DateTime.Now.AddSeconds(ReportDelaySeconds) - ReportMaxDelayTime.Value).TotalSeconds);
}

if (ReportTimer != null) {
ReportTimer.Change(Timeout.Infinite, Timeout.Infinite);
ReportTimer.Dispose();
}

ReportTimer = new Timer(async _ => await Send().ConfigureAwait(false), null, TimeSpan.FromSeconds(ReportDelaySeconds), Timeout.InfiniteTimeSpan);
ReportTimer = new Timer(async _ => await Send().ConfigureAwait(false), null, TimeSpan.FromSeconds(ReportDelaySeconds - delayCorrectionSeconds), Timeout.InfiniteTimeSpan);

if (ReportMaxDelayTime == null) {
ReportMaxDelayTime = DateTime.Now.AddSeconds(ReportMaxDelaySeconds);
}
} finally {
ReportSemaphore.Release();
}
}

private async Task Send() {
internal async Task Send() {
await ReportSemaphore.WaitAsync().ConfigureAwait(false);
try {
ReportTimer?.Dispose();
ReportMaxDelayTime = null;

List<string> messages = new List<string>();
List<Bot> bots = Reports.Keys.OrderBy(bot => bot.BotName).ToList();

Expand All @@ -101,6 +120,10 @@ private async Task Send() {
}
}

if (messages.Count == 0) {
return;
}

Bot? sender = SenderSteamID == 0 ? null : Bot.BotsReadOnly?.Values.FirstOrDefault(bot => bot.SteamID == SenderSteamID);
if (sender == null
|| RecipientSteamID == 0
Expand Down

0 comments on commit 000f351

Please sign in to comment.