Skip to content

Commit

Permalink
Merge pull request #294 from rGunti/293-bug-counter-commands-missing
Browse files Browse the repository at this point in the history
implement easier counter commands
  • Loading branch information
rGunti authored Jun 17, 2024
2 parents 50c17db + 72093e8 commit ceb3f23
Show file tree
Hide file tree
Showing 8 changed files with 189 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,16 @@ public CommandResult SetCounter(
return CreateCounterResult(formatParams with { Counter = 0 });
}

// For convenience, "+/-" without a number is read as "+/- 1"
if (operationOrValue == "+")
{
operationOrValue = "+1";
}
else if (operationOrValue == "-")
{
operationOrValue = "-1";
}

if (
(operationOrValue.StartsWith("+") || operationOrValue.StartsWith("-"))
&& int.TryParse(operationOrValue, out int incrementValue)
Expand Down
79 changes: 69 additions & 10 deletions src/FloppyBot.Commands.Custom.Execution/CustomCommandExecutor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -167,18 +167,19 @@ IEnumerable<string> commandNames
CommandResponse response
)
{
var placeholderContainer = new PlaceholderContainer(
instruction,
description,
_timeProvider.GetCurrentUtcTime(),
_randomNumberGenerator.Next(0, 100),
_counterStorageService,
HandleCounterCommands(instruction, description)
);

switch (response.Type)
{
case ResponseType.Text:
return response.Content.Format(
new PlaceholderContainer(
instruction,
description,
_timeProvider.GetCurrentUtcTime(),
_randomNumberGenerator.Next(0, 100),
_counterStorageService
)
);
return response.Content.Format(placeholderContainer);
case ResponseType.Sound:
string[] split = response.Content.Split(CommandResponse.SOUND_CMD_SPLIT_CHAR);
string payloadName = split[0];
Expand All @@ -193,9 +194,67 @@ CommandResponse response
_timeProvider.GetCurrentUtcTime()
)
);
return reply;
return reply?.Format(placeholderContainer);
default:
throw new NotImplementedException($"Response Type {response.Type} not implemented");
}
}

private int? HandleCounterCommands(
CommandInstruction instruction,
CustomCommandDescription description
)
{
var authorPrivilegeLevel = instruction.Context!.SourceMessage.Author.PrivilegeLevel;
if (instruction.Parameters.Count < 1 || authorPrivilegeLevel < PrivilegeLevel.Moderator)
{
_logger.LogTrace(
"No counter operation specified or user does not have the required privilege level (had {AuthorPrivilegeLevel}), nothing to do",
authorPrivilegeLevel
);
return null;
}

var firstArg = instruction.Parameters[0].Trim();
if (string.IsNullOrEmpty(instruction.Parameters[0]))
{
_logger.LogTrace("First Argument was empty, ignoring");
return null;
}

var prefix = firstArg[..1];
if (prefix != "+" && prefix != "-")
{
_logger.LogTrace("First Argument did not start with + or -, ignoring");
return null;
}

var number = firstArg[1..];
if (number.Length == 0)
{
number = "1";
}

if (!int.TryParse(number, out var increment))
{
_logger.LogWarning(
"Could not parse number, ignoring (Input was: {InputArgument})",
firstArg
);
return null;
}

switch (prefix)
{
case "+":
_logger.LogDebug("Found +, increasing counter by {IncrementValue}", increment);
return _counterStorageService.Increase(description.Id, increment);
case "-":
_logger.LogDebug("Found -, decreasing counter by {IncrementValue}", increment);
return _counterStorageService.Increase(description.Id, -increment);
}

_logger.LogTrace("No valid input found, ignoring (Input was: {InputArgument})", firstArg);
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ public PlaceholderContainer(
CustomCommandDescription commandDescription,
DateTimeOffset now,
int newRandomNumber,
ICounterStorageService counterStorageService
ICounterStorageService counterStorageService,
int? newCounterValue = null
)
{
_commandInstruction = instruction;
Expand All @@ -41,6 +42,7 @@ ICounterStorageService counterStorageService
Random = newRandomNumber;

_counterStorageService = counterStorageService;
_newCounterValue = newCounterValue;
}

public string Caller => _commandInstruction.Context!.SourceMessage.Author.DisplayName;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ public IImmutableList<CommandResponse> Responses

public CommandResponseMode ResponseMode { get; init; }

public bool AllowCounterOperations { get; init; }

public bool IsSoundCommand =>
Aliases.Count == 0 && Responses.All(r => r.Type == ResponseType.Sound);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ public record CustomCommandDescriptionEo : IEntity<CustomCommandDescriptionEo>
public CommandResponseEo[] Responses { get; set; }
public CommandLimitationEo Limitations { get; set; }
public CommandResponseMode ResponseMode { get; init; }
public bool AllowCounterOperations { get; set; }
public string Id { get; set; }

public CustomCommandDescriptionEo WithId(string newId)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ public CustomCommandStorageProfile()
.ToImmutableList(),
Limitations = ctx.Mapper.Map<CommandLimitation>(eo.Limitations),
ResponseMode = eo.ResponseMode,
AllowCounterOperations = eo.AllowCounterOperations,
}
);
CreateMap<CommandResponseEo, CommandResponse>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using FloppyBot.Base.Clock;
using FloppyBot.Base.EquatableCollections;
using FloppyBot.Base.Rng;
using FloppyBot.Base.Storage.LiteDb;
using FloppyBot.Chat;
using FloppyBot.Chat.Entities;
using FloppyBot.Commands.Core.Cooldown;
Expand Down Expand Up @@ -57,6 +58,16 @@ public class CustomCommandExecutorTests
)
);

private readonly ICounterStorageService _counterStorageService;

public CustomCommandExecutorTests()
{
var liteDb = LiteDbRepositoryFactory.CreateMemoryInstance();
_counterStorageService = A.Fake<ICounterStorageService>(x =>
x.Wrapping(new CounterStorageService(liteDb))
);
}

[DataTestMethod]
[DataRow(0, false)]
[DataRow(5, false)]
Expand Down Expand Up @@ -114,7 +125,7 @@ bool expectResult
timeProvider,
new RandomNumberGenerator(),
cooldownService,
A.Fake<ICounterStorageService>(),
_counterStorageService,
A.Fake<ISoundCommandInvocationSender>()
);

Expand Down Expand Up @@ -151,16 +162,13 @@ public void HandlesCounterCorrectly()
var timeProvider = new FixedTimeProvider(RefTime.Add(5.Seconds()));

var cooldownService = A.Fake<ICooldownService>();
var counterService = A.Fake<ICounterStorageService>();
var counter = 0;
A.CallTo(() => counterService.Next(CommandDescription.Id)).ReturnsLazily(() => ++counter);

var executor = new CustomCommandExecutor(
NullLogger<CustomCommandExecutor>.Instance,
timeProvider,
new RandomNumberGenerator(),
cooldownService,
counterService,
_counterStorageService,
A.Fake<ISoundCommandInvocationSender>()
);

Expand All @@ -177,7 +185,8 @@ CommandDescription with
)
.ToArray();

A.CallTo(() => counterService.Next(CommandDescription.Id)).MustHaveHappenedOnceExactly();
A.CallTo(() => _counterStorageService.Next(CommandDescription.Id))
.MustHaveHappenedOnceExactly();
Assert.AreEqual("I am at level 1 now!", reply.First());

// Repeat
Expand Down Expand Up @@ -210,7 +219,7 @@ public void HandlesUserLimitationCorrectly(string inputUser, bool expectResult)
timeProvider,
new RandomNumberGenerator(),
cooldownService,
A.Fake<ICounterStorageService>(),
_counterStorageService,
A.Fake<ISoundCommandInvocationSender>()
);

Expand Down Expand Up @@ -252,42 +261,114 @@ CommandDescription with
[TestMethod]
public void CounterWillOnlyIncrementOnce()
{
var counterService = A.Fake<ICounterStorageService>();
var counter = 0;
A.CallTo(() => counterService.Next(CommandDescription.Id)).ReturnsLazily(() => ++counter);
A.CallTo(() => _counterStorageService.Next(CommandDescription.Id))
.ReturnsLazily(() => ++counter);
var placeholder = new PlaceholderContainer(
CommandInstruction,
CommandDescription,
RefTime,
42,
counterService
_counterStorageService
);

// Access counter
Assert.AreEqual(1, placeholder.Counter);
A.CallTo(() => counterService.Next(CommandDescription.Id)).MustHaveHappenedOnceExactly();
A.CallTo(() => _counterStorageService.Next(CommandDescription.Id))
.MustHaveHappenedOnceExactly();

// Access counter again (ensure it has not been called twice)
Assert.AreEqual(1, placeholder.Counter);
A.CallTo(() => counterService.Next(CommandDescription.Id)).MustHaveHappenedOnceExactly();
A.CallTo(() => _counterStorageService.Next(CommandDescription.Id))
.MustHaveHappenedOnceExactly();
}

[TestMethod]
public void PeekCounterWillNotIncreaseCounterValue()
{
var counterService = A.Fake<ICounterStorageService>();
A.CallTo(() => counterService.Peek(A<string>.Ignored)).ReturnsLazily(() => 1);
_counterStorageService.Set(CommandDescription.Id, 1);
var placeholder = new PlaceholderContainer(
CommandInstruction,
CommandDescription,
RefTime,
42,
counterService
_counterStorageService
);

// Access counter
Assert.AreEqual(1, placeholder.PeekCounter);
A.CallTo(() => counterService.Peek(A<string>.Ignored)).MustHaveHappenedOnceExactly();
A.CallTo(() => counterService.Next(A<string>.Ignored)).MustNotHaveHappened();
A.CallTo(() => _counterStorageService.Peek(A<string>.Ignored))
.MustHaveHappenedOnceExactly();
A.CallTo(() => _counterStorageService.Next(A<string>.Ignored)).MustNotHaveHappened();
}

[TestMethod]
public void AssumingOperationsAllowed_CounterWillIncrementWithCorrectCommand()
{
var timeProvider = new FixedTimeProvider(RefTime.Add(5.Seconds()));

var cooldownService = A.Fake<ICooldownService>();
var counterService = _counterStorageService;
_counterStorageService.Set(CommandDescription.Id, 10);

var executor = new CustomCommandExecutor(
NullLogger<CustomCommandExecutor>.Instance,
timeProvider,
new RandomNumberGenerator(),
cooldownService,
counterService,
A.Fake<ISoundCommandInvocationSender>()
);

var customizedCommandDescription = CommandDescription with
{
Responses = new[]
{
new CommandResponse(ResponseType.Text, "I am at level {PeekCounter} now!"),
}.ToImmutableListWithValueSemantics(),
AllowCounterOperations = true,
};

string?[] reply = executor
.Execute(
CommandInstruction with
{
Parameters = new[] { "+" }.ToImmutableListWithValueSemantics(),
Context = new CommandContext(
SourceMessage: CommandInstruction.Context!.SourceMessage with
{
Author = CommandInstruction.Context!.SourceMessage.Author with
{
PrivilegeLevel = PrivilegeLevel.Moderator,
},
}
),
},
customizedCommandDescription
)
.ToArray();

Assert.AreEqual("I am at level 11 now!", reply.First());

// Repeat
reply = executor
.Execute(
CommandInstruction with
{
Parameters = new[] { "-" }.ToImmutableListWithValueSemantics(),
Context = new CommandContext(
SourceMessage: CommandInstruction.Context!.SourceMessage with
{
Author = CommandInstruction.Context!.SourceMessage.Author with
{
PrivilegeLevel = PrivilegeLevel.Moderator,
},
}
),
},
customizedCommandDescription
)
.ToArray();
Assert.AreEqual("I am at level 10 now!", reply.First());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ public record CustomCommandDto(
CommandResponseDto[] Responses,
CommandLimitationDto Limitations,
CommandResponseMode ResponseMode,
CounterValueDto? Counter
CounterValueDto? Counter,
bool AllowCounterOperations
)
{
public static CustomCommandDto FromEntity(CustomCommandDescription entity, int? counter)
Expand All @@ -23,7 +24,8 @@ public static CustomCommandDto FromEntity(CustomCommandDescription entity, int?
entity.Responses.Select(CommandResponseDto.FromEntity).ToArray(),
CommandLimitationDto.FromEntity(entity.Limitations),
ToEntityMode(entity.ResponseMode),
counter.HasValue ? new CounterValueDto(counter.Value) : null
counter.HasValue ? new CounterValueDto(counter.Value) : null,
entity.AllowCounterOperations
);
}

Expand All @@ -37,6 +39,7 @@ public CustomCommandDescription ToEntity()
Responses = Responses.Select(r => r.ToEntity()).ToImmutableListWithValueSemantics(),
Limitations = Limitations.ToEntity(),
ResponseMode = ResponseMode.ToEntity(),
AllowCounterOperations = AllowCounterOperations,
};
}

Expand Down

0 comments on commit ceb3f23

Please sign in to comment.