111 lines
3.4 KiB
C#
111 lines
3.4 KiB
C#
using System.Text.RegularExpressions;
|
|
using GmRelay.Shared.Domain;
|
|
|
|
namespace GmRelay.Bot.Features.Sessions.RescheduleSession;
|
|
|
|
internal sealed record RescheduleVotingInput(
|
|
IReadOnlyList<DateTimeOffset> Options,
|
|
DateTimeOffset Deadline)
|
|
{
|
|
private static readonly Regex DateTimePattern = new(
|
|
@"(?<date>\d{1,2}\.\d{2}\.\d{4})\s+(?<time>\d{1,2}:\d{2})",
|
|
RegexOptions.CultureInvariant);
|
|
|
|
public static bool TryParse(
|
|
string text,
|
|
DateTimeOffset nowUtc,
|
|
out RescheduleVotingInput input,
|
|
out string error)
|
|
{
|
|
input = new RescheduleVotingInput([], default);
|
|
error = string.Empty;
|
|
|
|
var options = new List<DateTimeOffset>();
|
|
DateTimeOffset? deadline = null;
|
|
|
|
foreach (var rawLine in text.Split('\n', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries))
|
|
{
|
|
var line = rawLine.Trim();
|
|
var match = DateTimePattern.Match(line);
|
|
if (!match.Success)
|
|
continue;
|
|
|
|
var value = $"{match.Groups["date"].Value} {match.Groups["time"].Value}";
|
|
if (!MoscowTime.TryParseMoscow(value, out var parsed))
|
|
continue;
|
|
|
|
if (IsDeadlineLine(line))
|
|
{
|
|
deadline = parsed;
|
|
}
|
|
else
|
|
{
|
|
options.Add(parsed);
|
|
}
|
|
}
|
|
|
|
if (options.Count is < 2 or > 3)
|
|
{
|
|
error = "Укажите от 2 до 3 вариантов времени.";
|
|
return false;
|
|
}
|
|
|
|
if (options.Distinct().Count() != options.Count)
|
|
{
|
|
error = "Варианты времени не должны повторяться.";
|
|
return false;
|
|
}
|
|
|
|
if (deadline is null)
|
|
{
|
|
error = "Укажите дедлайн голосования строкой «Дедлайн: ДД.ММ.ГГГГ ЧЧ:ММ».";
|
|
return false;
|
|
}
|
|
|
|
if (options.Any(option => option <= nowUtc))
|
|
{
|
|
error = "Все варианты времени должны быть в будущем.";
|
|
return false;
|
|
}
|
|
|
|
if (deadline.Value <= nowUtc)
|
|
{
|
|
error = "Дедлайн голосования должен быть в будущем.";
|
|
return false;
|
|
}
|
|
|
|
if (deadline.Value >= options.Min())
|
|
{
|
|
error = "Дедлайн голосования должен быть раньше первого предложенного времени.";
|
|
return false;
|
|
}
|
|
|
|
input = new RescheduleVotingInput(options, deadline.Value);
|
|
return true;
|
|
}
|
|
|
|
private static bool IsDeadlineLine(string line)
|
|
{
|
|
var normalized = line.TrimStart('-', '*', ' ', '\t').ToLowerInvariant();
|
|
|
|
return normalized.StartsWith("дедлайн", StringComparison.Ordinal)
|
|
|| normalized.StartsWith("deadline", StringComparison.Ordinal)
|
|
|| normalized.StartsWith("до:", StringComparison.Ordinal);
|
|
}
|
|
}
|
|
|
|
internal sealed record RescheduleOptionDto(
|
|
Guid OptionId,
|
|
int DisplayOrder,
|
|
DateTimeOffset ProposedAt);
|
|
|
|
internal sealed record RescheduleOptionVoteDto(
|
|
Guid OptionId,
|
|
Guid PlayerId,
|
|
string DisplayName,
|
|
string? TelegramUsername);
|
|
|
|
internal sealed record RescheduleOptionVoteCount(
|
|
Guid OptionId,
|
|
int VoteCount);
|