using GmRelay.Shared.Domain; namespace GmRelay.Bot.Features.Sessions.CreateSession; internal sealed record NewSessionParseResult( string? Title, string? Link, int? MaxPlayers, IReadOnlyList ScheduledTimes, IReadOnlyList PastTimeInputs, IReadOnlyList InvalidTimeInputs, IReadOnlyList InvalidSeatLimitInputs, IReadOnlyList InvalidRecurringInputs) { public bool IsValid => !string.IsNullOrWhiteSpace(Title) && !string.IsNullOrWhiteSpace(Link) && ScheduledTimes.Count > 0 && InvalidSeatLimitInputs.Count == 0 && InvalidRecurringInputs.Count == 0; } internal static class NewSessionCommandParser { private const int MaxRecurringSessionCount = 52; private const int MaxRecurringIntervalDays = 365; private const string TitlePrefix = "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435:"; private const string TimePrefix = "\u0412\u0440\u0435\u043c\u044f:"; private const string LinkPrefix = "\u0421\u0441\u044b\u043b\u043a\u0430:"; private static readonly string[] SeatLimitPrefixes = [ "\u041c\u0435\u0441\u0442:", "\u041b\u0438\u043c\u0438\u0442:", "\u041c\u0430\u043a\u0441\u0438\u043c\u0443\u043c:" ]; private static readonly string[] RecurringCountPrefixes = [ "\u0418\u0433\u0440:", "\u0421\u0435\u0441\u0441\u0438\u0439:", "\u041f\u043e\u0432\u0442\u043e\u0440\u043e\u0432:" ]; private static readonly string[] RecurringIntervalPrefixes = [ "\u0428\u0430\u0433:", "\u0418\u043d\u0442\u0435\u0440\u0432\u0430\u043b:" ]; public static NewSessionParseResult Parse(string? text, DateTimeOffset nowUtc) { string? title = null; string? link = null; int? maxPlayers = null; int? recurringCount = null; var recurringIntervalDays = 7; var scheduledTimes = new List(); var pastTimeInputs = new List(); var invalidTimeInputs = new List(); var invalidSeatLimitInputs = new List(); var invalidRecurringInputs = new List(); foreach (var line in (text ?? string.Empty).Split('\n', StringSplitOptions.TrimEntries)) { if (line.StartsWith(TitlePrefix, StringComparison.OrdinalIgnoreCase)) { title = line[TitlePrefix.Length..].Trim(); continue; } if (line.StartsWith(LinkPrefix, StringComparison.OrdinalIgnoreCase)) { link = line[LinkPrefix.Length..].Trim(); continue; } var seatLimitPrefix = SeatLimitPrefixes.FirstOrDefault(prefix => line.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)); if (seatLimitPrefix is not null) { var seatLimitInput = line[seatLimitPrefix.Length..].Trim(); if (int.TryParse(seatLimitInput, out var parsedMaxPlayers) && parsedMaxPlayers > 0) { maxPlayers = parsedMaxPlayers; } else { invalidSeatLimitInputs.Add(seatLimitInput); } continue; } var recurringCountPrefix = RecurringCountPrefixes.FirstOrDefault(prefix => line.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)); if (recurringCountPrefix is not null) { var recurringInput = line[recurringCountPrefix.Length..].Trim(); if (int.TryParse(recurringInput, out var parsedCount) && parsedCount is >= 1 and <= MaxRecurringSessionCount) { recurringCount = parsedCount; } else { invalidRecurringInputs.Add(recurringInput); } continue; } var recurringIntervalPrefix = RecurringIntervalPrefixes.FirstOrDefault(prefix => line.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)); if (recurringIntervalPrefix is not null) { var recurringInput = line[recurringIntervalPrefix.Length..].Trim(); if (int.TryParse(recurringInput, out var parsedInterval) && parsedInterval is >= 1 and <= MaxRecurringIntervalDays) { recurringIntervalDays = parsedInterval; } else { invalidRecurringInputs.Add(recurringInput); } continue; } if (!line.StartsWith(TimePrefix, StringComparison.OrdinalIgnoreCase)) { continue; } var timeInput = line[TimePrefix.Length..].Trim(); if (!MoscowTime.TryParseMoscow(timeInput, out var scheduledAt)) { invalidTimeInputs.Add(timeInput); continue; } if (scheduledAt <= nowUtc) { pastTimeInputs.Add(timeInput); continue; } scheduledTimes.Add(scheduledAt); } if (recurringCount.HasValue && scheduledTimes.Count == 1) { var firstScheduledTime = scheduledTimes[0]; scheduledTimes = Enumerable.Range(0, recurringCount.Value) .Select(index => firstScheduledTime.AddDays(recurringIntervalDays * index)) .ToList(); } return new NewSessionParseResult( title, link, maxPlayers, scheduledTimes, pastTimeInputs, invalidTimeInputs, invalidSeatLimitInputs, invalidRecurringInputs); } }