diff --git a/src/DevChatter.Bot.Core/Automation/AutomatedActionFactory.cs b/src/DevChatter.Bot.Core/Automation/AutomatedActionFactory.cs new file mode 100644 index 00000000..57b2ffd1 --- /dev/null +++ b/src/DevChatter.Bot.Core/Automation/AutomatedActionFactory.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Text; +using DevChatter.Bot.Core.Games.DealNoDeal; +using DevChatter.Bot.Core.Systems.Chat; +using DevChatter.Bot.Core.Util; + +namespace DevChatter.Bot.Core.Automation +{ + public class AutomatedActionFactory : IAutomatedActionFactory + { + private readonly DealNoDealGame _dealNoDealGame; + private readonly IChatClient _chatClient; + + public AutomatedActionFactory(DealNoDealGame dealNoDealGame, IChatClient chatClient) + { + _dealNoDealGame = dealNoDealGame; + _chatClient = chatClient; + } + + public IIntervalAction GetIntervalAction(DealNoDealGameState gameState) + { + switch (gameState) + { + case DealNoDealGameState.ChosingStartingBoxes: + { + return new ChoseStartingBoxesAction(_dealNoDealGame, _chatClient); + } + case DealNoDealGameState.PickingBoxes: + { + return new PickBoxesAction(_dealNoDealGame, _chatClient); + } + case DealNoDealGameState.MakingADeal: + { + return new OfferDealAction(_dealNoDealGame, _chatClient); + } + default: + { + return null; + } + } + } + } +} diff --git a/src/DevChatter.Bot.Core/Automation/ChoseStartingBoxesAction.cs b/src/DevChatter.Bot.Core/Automation/ChoseStartingBoxesAction.cs new file mode 100644 index 00000000..a79aedeb --- /dev/null +++ b/src/DevChatter.Bot.Core/Automation/ChoseStartingBoxesAction.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.Text; +using DevChatter.Bot.Core.Data.Model; +using DevChatter.Bot.Core.Games.DealNoDeal; +using DevChatter.Bot.Core.Systems.Chat; +using DevChatter.Bot.Core.Util; + + +namespace DevChatter.Bot.Core.Automation +{ + public class ChoseStartingBoxesAction : IIntervalAction + { + private readonly IClock _clock; + private DateTime _nextRunTime; + private IChatClient _chatClient; + private DealNoDealGame _dealNoDealGame; + + + public ChoseStartingBoxesAction(DealNoDealGame dealNoDealGame, IChatClient chatClient) + { + _dealNoDealGame = dealNoDealGame; + _chatClient = chatClient; + _clock = new SystemClock(); + _chatClient.SendMessage($"everyone has {DealNoDealGame.SECONDS_TO_CHOOSE_BOXES} seconds to choose a box!"); + SetNextRunTime(); + } + + public string Name { get; } = nameof(ChoseStartingBoxesAction); + public bool IsTimeToRun() => _clock.Now >= _nextRunTime; + + public void Invoke() + { + _nextRunTime = _clock.Now.AddYears(1); + _chatClient.SendMessage("Finished picking starting boxes"); + + + _dealNoDealGame.EnsureMinPlayableBoxes(); + _chatClient.SendMessage("Game Started!"); + _dealNoDealGame.GameState = DealNoDealGameState.PickingBoxes; + _dealNoDealGame.SetActionForGameState(DealNoDealGameState.PickingBoxes); + } + + private void SetNextRunTime() + { + _nextRunTime = _clock.Now.AddSeconds(DealNoDealGame.SECONDS_TO_CHOOSE_BOXES); + } + } +} diff --git a/src/DevChatter.Bot.Core/Automation/DelayedMessageAction.cs b/src/DevChatter.Bot.Core/Automation/DelayedMessageAction.cs index f9562284..4ef8f382 100644 --- a/src/DevChatter.Bot.Core/Automation/DelayedMessageAction.cs +++ b/src/DevChatter.Bot.Core/Automation/DelayedMessageAction.cs @@ -5,7 +5,6 @@ namespace DevChatter.Bot.Core.Automation { public class DelayedMessageAction : IIntervalAction { - private readonly string _message; private readonly IChatClient _chatClient; private DateTime _nextRunTime; public TimeSpan DelayTimeSpan; @@ -13,18 +12,20 @@ public class DelayedMessageAction : IIntervalAction public DelayedMessageAction(int delayInSeconds, string message, IChatClient chatClient, string name) { DelayTimeSpan = TimeSpan.FromSeconds(delayInSeconds); - _message = message; + Message = message; _chatClient = chatClient; Name = name; _nextRunTime = DateTime.Now.AddSeconds(delayInSeconds); } public string Name { get; } + public string Message { get; set; } + public bool IsTimeToRun() => DateTime.Now > _nextRunTime; public void Invoke() { - _chatClient.SendMessage(_message); + _chatClient.SendMessage(Message); _nextRunTime = DateTime.MaxValue; } } diff --git a/src/DevChatter.Bot.Core/Automation/IAutomatedActionFactory.cs b/src/DevChatter.Bot.Core/Automation/IAutomatedActionFactory.cs new file mode 100644 index 00000000..94883b5c --- /dev/null +++ b/src/DevChatter.Bot.Core/Automation/IAutomatedActionFactory.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Text; +using DevChatter.Bot.Core.Games.DealNoDeal; +using DevChatter.Bot.Core.Systems.Chat; + +namespace DevChatter.Bot.Core.Automation +{ + public interface IAutomatedActionFactory + { + IIntervalAction GetIntervalAction(DealNoDealGameState gameState); + } +} diff --git a/src/DevChatter.Bot.Core/Automation/OfferDealAction.cs b/src/DevChatter.Bot.Core/Automation/OfferDealAction.cs new file mode 100644 index 00000000..bc9de7b8 --- /dev/null +++ b/src/DevChatter.Bot.Core/Automation/OfferDealAction.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Text; +using DevChatter.Bot.Core.Games.DealNoDeal; +using DevChatter.Bot.Core.Systems.Chat; +using DevChatter.Bot.Core.Util; + +namespace DevChatter.Bot.Core.Automation +{ + public class OfferDealAction : IIntervalAction + { + private readonly IClock _clock; + private DateTime _nextRunTime; + private IChatClient _chatClient; + private DealNoDealGame _dealNoDealGame; + + + public OfferDealAction(DealNoDealGame dealNoDealGame, IChatClient chatClient) + { + _dealNoDealGame = dealNoDealGame; + _chatClient = chatClient; + _clock = new SystemClock(); + _dealNoDealGame.PrintBoxesRemaining(); + _chatClient.SendMessage($"***DEAL OFFER: {dealNoDealGame.DealOffer} TOKENS!"); + _chatClient.SendMessage(" type \"!dnd accept\" or \"!dnd decline\" to accept or decline offer "); + _chatClient.SendMessage($"You have {DealNoDealGame.SECONDS_TO_CHOOSE_BOXES} seconds or the deal will be automatically accepted"); + SetNextRunTime(); + } + + public string Name { get; } = nameof(OfferDealAction); + public bool IsTimeToRun() => _clock.Now >= _nextRunTime; + + + public void Invoke() + { + _chatClient.SendMessage($"Automatically accepting deal! {DealNoDealGame.SECONDS_TO_CHOOSE_BOXES} seconds passed"); + _dealNoDealGame.AcceptDeal(_chatClient); + } + + private void SetNextRunTime() + { + _nextRunTime = _clock.Now.AddSeconds(DealNoDealGame.SECONDS_TO_CHOOSE_BOXES); + } + } +} diff --git a/src/DevChatter.Bot.Core/Automation/PickBoxesAction.cs b/src/DevChatter.Bot.Core/Automation/PickBoxesAction.cs new file mode 100644 index 00000000..b7db7210 --- /dev/null +++ b/src/DevChatter.Bot.Core/Automation/PickBoxesAction.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.Text; +using DevChatter.Bot.Core.Games.DealNoDeal; +using DevChatter.Bot.Core.Systems.Chat; +using DevChatter.Bot.Core.Util; + +namespace DevChatter.Bot.Core.Automation +{ + public class PickBoxesAction : IIntervalAction + { + private readonly IClock _clock; + private DateTime _nextRunTime; + private IChatClient _chatClient; + private DealNoDealGame _dealNoDealGame; + + + public PickBoxesAction(DealNoDealGame dealNoDealGame, IChatClient chatClient) + { + _dealNoDealGame = dealNoDealGame; + _chatClient = chatClient; + _clock = new SystemClock(); + _dealNoDealGame.PrintBoxesRemaining(); + SetNextRunTime(); + } + + public string Name { get; } = nameof(PickBoxesAction); + public bool IsTimeToRun() => _clock.Now >= _nextRunTime; + + public void Invoke() + { + _chatClient.SendMessage("Picking a random Box. User did not respond"); + _dealNoDealGame.PickRandomBox(_dealNoDealGame.MainPlayer.DisplayName); + } + + private void SetNextRunTime() + { + _nextRunTime = _clock.Now.AddSeconds(DealNoDealGame.SECONDS_TO_CHOOSE_BOXES); + } + } +} diff --git a/src/DevChatter.Bot.Core/Games/DealNoDeal/Box.cs b/src/DevChatter.Bot.Core/Games/DealNoDeal/Box.cs new file mode 100644 index 00000000..4bd489c7 --- /dev/null +++ b/src/DevChatter.Bot.Core/Games/DealNoDeal/Box.cs @@ -0,0 +1,33 @@ +namespace DevChatter.Bot.Core.Games.DealNoDeal +{ + public class Box + { + public Box(int id, int value, string owner) + { + Id = id; + TokenValue = value; + Owner = owner; + } + + public Box(int id, int value) + { + Id = id; + TokenValue = value; + } + + public int Id { get; set; } + public int TokenValue { get; set; } + public string Owner { get; set; } + + public bool SetOwner(string ownerName) + { + if (Owner != null) + { + return false; + } + + Owner = ownerName; + return true; + } + } +} diff --git a/src/DevChatter.Bot.Core/Games/DealNoDeal/DNDCommand.cs b/src/DevChatter.Bot.Core/Games/DealNoDeal/DNDCommand.cs new file mode 100644 index 00000000..085dcc51 --- /dev/null +++ b/src/DevChatter.Bot.Core/Games/DealNoDeal/DNDCommand.cs @@ -0,0 +1,54 @@ +using DevChatter.Bot.Core.Commands; +using DevChatter.Bot.Core.Commands.Operations; +using DevChatter.Bot.Core.Data; +using DevChatter.Bot.Core.Data.Model; +using DevChatter.Bot.Core.Events.Args; +using DevChatter.Bot.Core.Systems.Chat; +using System.Collections.Generic; +using System.Linq; + +namespace DevChatter.Bot.Core.Games.DealNoDeal +{ + public class DNDCommand : BaseCommand, IGameCommand + { + private readonly DealNoDealGame _dealNoDealGame; + public IGame Game => _dealNoDealGame; + + public DNDCommand(IRepository repository, DealNoDealGame dealNoDealGame, IChatClient chatClient) : base(repository, UserRole.Everyone) + { + _dealNoDealGame = dealNoDealGame; + _chatClient = chatClient; + HelpText = + "Use \"!dnd\" to start a game. Use \"!dnd pick x\" to pick/guess a box. Use \"!dnd accept\" or \"!dnd decline\" to accept or decline offers ."; + } + private List _operations; + private readonly IChatClient _chatClient; + + public List Operations => _operations ?? (_operations = new List + { + new PickABoxOperation(_dealNoDealGame,_chatClient), + new MakeADealOperation(_dealNoDealGame,_chatClient) + }); + + protected override void HandleCommand(IChatClient chatClient, CommandReceivedEventArgs eventArgs) + { + string argumentOne = eventArgs?.Arguments?.FirstOrDefault(); + ChatUser chatUser = eventArgs?.ChatUser; + //dnd start dnd pick + var operationToUse = Operations.SingleOrDefault(x => x.ShouldExecute(argumentOne)); + if (operationToUse != null) + { + string resultMessage = operationToUse.TryToExecute(eventArgs); + } + else + { + if (string.IsNullOrWhiteSpace(argumentOne)) + { + // attempting to start a game + _dealNoDealGame.AttemptToStartGame(chatUser); + } + } + } + + } +} diff --git a/src/DevChatter.Bot.Core/Games/DealNoDeal/DealNoDealGame.cs b/src/DevChatter.Bot.Core/Games/DealNoDeal/DealNoDealGame.cs new file mode 100644 index 00000000..4d67316b --- /dev/null +++ b/src/DevChatter.Bot.Core/Games/DealNoDeal/DealNoDealGame.cs @@ -0,0 +1,342 @@ +using DevChatter.Bot.Core.Automation; +using DevChatter.Bot.Core.Data.Model; +using DevChatter.Bot.Core.Events; +using DevChatter.Bot.Core.Extensions; +using DevChatter.Bot.Core.Systems.Chat; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace DevChatter.Bot.Core.Games.DealNoDeal +{ + public class DealNoDealGame : IGame + { + public bool IsRunning { get; private set; } + + //Configuration + public const UserRole ROLE_REQUIRED_TO_START = UserRole.Everyone; + public const int SECONDS_TO_CHOOSE_BOXES = 30; + public const int TOKENS_TO_PLAY = 25; + public const int MIN_PLAYABLE_BOXES = 5; + //Change these if you don't like the rewards + private static readonly List InitialPrices = new List() + { + //15 at the moment + 0,1,2,4,6,8,10,15,18,20,25,32,50,64,100 + }; + + public List StartingBoxes; + public List BoxesWithOwners; + + public DealNoDealGameState GameState = DealNoDealGameState.GameNotRunning; + private IIntervalAction _actionBeingPerformed; + + public ChatUser MainPlayer; + public int DealOffer = 0; + + public DealNoDealGame(IChatClient chatClient, ICurrencyGenerator currencyGenerator, IAutomatedActionSystem automatedActionSystem) + { + _chatClient = chatClient; + _automatedActionFactory = new AutomatedActionFactory(this, _chatClient); + _currencyGenerator = currencyGenerator; + _automatedActionSystem = automatedActionSystem; + } + + private readonly IChatClient _chatClient; + private readonly ICurrencyGenerator _currencyGenerator; + private readonly IAutomatedActionSystem _automatedActionSystem; + private readonly AutomatedActionFactory _automatedActionFactory; + + + public void AttemptToStartGame(ChatUser chatUser) + { + + if (IsRunning) + { + SendGameAlreadyStartedMessage(_chatClient, chatUser); + return; + } + + if (!chatUser.IsInThisRoleOrHigher(ROLE_REQUIRED_TO_START)) + { + _chatClient.SendMessage( + $"You must be at least a {ROLE_REQUIRED_TO_START} to start a game, {chatUser.DisplayName}"); + return; + } + bool transactionComplete = ChargeTokensForStartingAGame(_chatClient, chatUser); + if (transactionComplete) + { + MainPlayer = chatUser; + IsRunning = true; + BoxesWithOwners = new List(); + StartingBoxes = ShuffleBoxes(); + _chatClient.SendMessage( + $"{MainPlayer.DisplayName} started a Deal or No Deal game! Please choose a box everyone by typing \"!dnd pick x\" ... Pick a box between numbers 1 and 15"); + + GameState = DealNoDealGameState.ChosingStartingBoxes; + SetActionForGameState(DealNoDealGameState.ChosingStartingBoxes); + } + else + { + _chatClient.SendMessage($"Not enough tokens to start a game, {chatUser}"); + } + + } + + public void SetActionForGameState(DealNoDealGameState gameState) + { + if (gameState == DealNoDealGameState.GameNotRunning) + { + _automatedActionSystem.RemoveAction(_actionBeingPerformed); + return; + } + //Remove all Game-actions and start a new one + _automatedActionSystem.RemoveAction(_actionBeingPerformed); + _actionBeingPerformed = _automatedActionFactory.GetIntervalAction(gameState); + _automatedActionSystem.AddAction(_actionBeingPerformed); + } + + public void QuitGame(IChatClient chatClient) + { + IsRunning = false; + MainPlayer = null; + DealOffer = 0; + GameState = DealNoDealGameState.GameNotRunning; + SetActionForGameState(DealNoDealGameState.GameNotRunning); + } + + private void SendGameAlreadyStartedMessage(IChatClient chatClient, ChatUser chatUser) + { + chatClient.SendMessage( + $"There is already a {nameof(DealNoDealGame)} running by {MainPlayer.DisplayName}, {chatUser.DisplayName}. Wait for the game to finish!"); + } + + private void SendGameNotStartedMessage(IChatClient chatClient, ChatUser chatUser) + { + chatClient.SendMessage($"There's no {nameof(DealNoDealGame)} running, {chatUser.DisplayName}."); + } + + private bool ChargeTokensForStartingAGame(IChatClient chatClient, ChatUser chatUser) + { + chatClient.SendMessage($"{TOKENS_TO_PLAY} tokens are being charged for starting a game."); + return _currencyGenerator.RemoveCurrencyFrom(chatUser.DisplayName, TOKENS_TO_PLAY); + } + + public void UpdateGameState() + { + if (CheckIfGameIsWon()) + { + return; + } + + //Note: if the box count is between 2 and totalcount - 2 + int totalPlayableBoxesCount = InitialPrices.Count - StartingBoxes.Count; + bool shouldOfferDeal = (BoxesWithOwners.Count < totalPlayableBoxesCount - 2) && + (BoxesWithOwners.Count != 1) && + (BoxesWithOwners.Count % 2 == 0); + if (shouldOfferDeal) + { + DealOffer = GetADeal(); + GameState = DealNoDealGameState.MakingADeal; + SetActionForGameState(DealNoDealGameState.MakingADeal); + } + else + { + //Restart timer + GameState = DealNoDealGameState.PickingBoxes; + SetActionForGameState(DealNoDealGameState.PickingBoxes); + } + } + + //Note: We can randomize this as we please + private int GetADeal() + { + //average + int result = BoxesWithOwners.Sum(b => b.TokenValue) / BoxesWithOwners.Count; + + //randomize offer + Random r = new Random(); + bool shouldSubtract = r.Next(2) == 0; + int value = r.Next(0, 5); + //if the offer is 10. the offer would be subtracted or added either 5 or 15 + if (shouldSubtract) + { + result -= value; + } + else + { + result += value; + } + + //Ensure offer is > 0 + if (result <= 0) + { + result = 1; + } + return result; + } + + public void AcceptDeal(IChatClient chatClient) + { + _currencyGenerator.AddCurrencyTo(MainPlayer.DisplayName, DealOffer); + chatClient.SendMessage($" {MainPlayer.DisplayName} ACCEPTED THE OFFER!"); + chatClient.SendMessage($"GAME OVER! {MainPlayer.DisplayName} recieved {DealOffer} tokens!"); + DealOffer = 0; + QuitGame(chatClient); + } + + public void DeclineDeal(IChatClient chatClient) + { + chatClient.SendMessage($"{MainPlayer.DisplayName} declined the Deal of {DealOffer} tokens!"); + DealOffer = 0; + GameState = DealNoDealGameState.PickingBoxes; + SetActionForGameState(DealNoDealGameState.PickingBoxes); + } + + public bool CheckIfGameIsWon() + { + if (BoxesWithOwners.Count != 1) + { + return false; + } + + int lastTokens = BoxesWithOwners.Single().TokenValue; + _chatClient.SendMessage($"GAME FINISHED!"); + _chatClient.SendMessage($"A LAST BOX WITH {lastTokens} TOKENS REMAINS!"); + _chatClient.SendMessage($"{MainPlayer.DisplayName} recieved {lastTokens} tokens! "); + _currencyGenerator.AddCurrencyTo(MainPlayer.DisplayName, lastTokens); + QuitGame(_chatClient); + return true; + } + + private List ShuffleBoxes() + { + List shuffledBoxes = new List(); + List pricesToShuffle = new List(InitialPrices); + Random r = new Random(); + int totalBoxesCount = InitialPrices.Count; + + for (int i = 0; i < totalBoxesCount; i++) + { + //get a new random with a range of left indexes + int indexToRemoveAt = r.Next(totalBoxesCount - i); + shuffledBoxes.Add(new Box(i, pricesToShuffle[indexToRemoveAt])); + pricesToShuffle.RemoveAt(indexToRemoveAt); + } + return shuffledBoxes; + } + + public void PickRandomBox(string displayName) + { + Random randy = new Random(); + Box randomBox = BoxesWithOwners[randy.Next(BoxesWithOwners.Count)]; + // TODO: Do something with this variable or don't store it here. + string boxValue = PickABox(randomBox.Owner, displayName); + } + + public string PickABox(string namePicked, string displayName) + { + Box chosenBox = BoxesWithOwners.Find(b => b.Owner.EqualsIns(namePicked)); + if (chosenBox == null) + { + _chatClient.SendMessage("That user does not hold a box, please chose another one"); + return null; + } + + //OpenBox + _chatClient.SendMessage($"{displayName}, opened a box with a value of {chosenBox.TokenValue}"); + BoxesWithOwners.Remove(chosenBox); + UpdateGameState(); + + return GetBoxValue(chosenBox.TokenValue); + } + + public void PrintBoxesRemaining() + { + string allPrices = "Prices left: "; + + //Printe all the prices left in order + foreach (var boxPrice in InitialPrices) + { + if (BoxesWithOwners.SingleOrDefault(b => b.TokenValue == boxPrice) != null) + { + allPrices += $"[{boxPrice}]"; + } + } + _chatClient.SendMessage(allPrices); + + //print all randomized boxes remaining + string allBoxes = $"Boxes remaining: {BoxesWithOwners.Count}. choose from the following: "; + foreach (var box in BoxesWithOwners) + { + allBoxes += "[" + box.Owner + "] "; + } + allBoxes += $" type \"!dnd pick x\" "; + _chatClient.SendMessage(allBoxes); + } + + public void EnsureMinPlayableBoxes() + { + if (StartingBoxes.Count <= 0) return; + if (BoxesWithOwners.Count > MIN_PLAYABLE_BOXES) return; + Random randy = new Random(); + for (int i = BoxesWithOwners.Count; i < 5; i++) + { + int index = randy.Next(StartingBoxes.Count); + Box box = StartingBoxes[index]; + BoxesWithOwners.Add(new Box(box.Id, box.TokenValue, "RandomChoice" + (i + 1))); + StartingBoxes.Remove(box); + } + } + + public string GetBoxValue(int tokenValue) + { + /* + * 33% chance that you will get lied. + * The tokenvalue will either be between some values or the exact one + */ + string result = ""; + Random randy = new Random(); + bool shouldLie = randy.Next(3) == 0; + if (shouldLie) + { + //Lie to the user + var randomBoxValue = BoxesWithOwners[randy.Next(BoxesWithOwners.Count)].TokenValue; + result = GetAproximateValue(randomBoxValue); + } + else + { + //dont lie but tell him aproximately + result = GetAproximateValue(tokenValue); + } + return result; + } + + public string GetAproximateValue(int value) + { + //NOTE: This just spits out a value around -20 value +20 or the value + string result = ""; + Random randy = new Random(); + + int r = randy.Next(10); + //every 4th value should be concrete + if (r % 4 == 0) + { + //return the exact one + return value.ToString(); + } + + //return aproximate value + int lowerValue = value - r - randy.Next(10); + if (lowerValue < 0) + { + lowerValue = 0; + } + + int higherValue = value + r + randy.Next(10); + result = "Between " + (lowerValue).ToString() + " - " + (higherValue).ToString(); + + return result; + } + } +} diff --git a/src/DevChatter.Bot.Core/Games/DealNoDeal/DealNoDealGameState.cs b/src/DevChatter.Bot.Core/Games/DealNoDeal/DealNoDealGameState.cs new file mode 100644 index 00000000..645a4176 --- /dev/null +++ b/src/DevChatter.Bot.Core/Games/DealNoDeal/DealNoDealGameState.cs @@ -0,0 +1,10 @@ +namespace DevChatter.Bot.Core.Games.DealNoDeal +{ + public enum DealNoDealGameState + { + GameNotRunning, + ChosingStartingBoxes, + PickingBoxes, + MakingADeal, + } +} diff --git a/src/DevChatter.Bot.Core/Games/DealNoDeal/MakeADealOperation.cs b/src/DevChatter.Bot.Core/Games/DealNoDeal/MakeADealOperation.cs new file mode 100644 index 00000000..f4c00318 --- /dev/null +++ b/src/DevChatter.Bot.Core/Games/DealNoDeal/MakeADealOperation.cs @@ -0,0 +1,64 @@ +using DevChatter.Bot.Core.Commands.Operations; +using DevChatter.Bot.Core.Events.Args; +using DevChatter.Bot.Core.Systems.Chat; +using System.Collections.Generic; + +namespace DevChatter.Bot.Core.Games.DealNoDeal +{ + public class MakeADealOperation : BaseCommandOperation + { + private readonly DealNoDealGame _dealNoDealGame; + private readonly IChatClient _chatClient; + + public MakeADealOperation(DealNoDealGame dealNoDealGame, IChatClient chatClient) + { + _chatClient = chatClient; + _dealNoDealGame = dealNoDealGame; + } + + public override List OperandWords { get; } = new List() {"accept", "decline"}; + public override string HelpText { get; } = "Use \"!dnd accept\" or \"!dnd decline\" to accept or decline the offer."; + + public override string TryToExecute(CommandReceivedEventArgs eventArgs) + { + if (!_dealNoDealGame.IsRunning) + { + return null; + } + + bool isMainPlayer = _dealNoDealGame.MainPlayer != eventArgs.ChatUser; + string pick = eventArgs.Arguments[0]; + if (string.IsNullOrWhiteSpace(pick)) + { + _chatClient.SendMessage(HelpText); + return null; + } + if (_dealNoDealGame.GameState == DealNoDealGameState.MakingADeal) + { + //Note: Only other players can choose a starting box + if (isMainPlayer) + { + + if (_dealNoDealGame.DealOffer == 0) + { + _chatClient.SendMessage("Something went wrong, the deal offer is 0"); + return ""; + } + + if (pick == "accept") + { + _dealNoDealGame.AcceptDeal(_chatClient); + return "Deal Accepted"; + } + + if(pick == "decline") + { + _dealNoDealGame.DeclineDeal(_chatClient); + return "Deal Declined"; + } + } + } + return ""; + } + } +} diff --git a/src/DevChatter.Bot.Core/Games/DealNoDeal/PickABoxOperation.cs b/src/DevChatter.Bot.Core/Games/DealNoDeal/PickABoxOperation.cs new file mode 100644 index 00000000..c156d72e --- /dev/null +++ b/src/DevChatter.Bot.Core/Games/DealNoDeal/PickABoxOperation.cs @@ -0,0 +1,83 @@ +using DevChatter.Bot.Core.Commands.Operations; +using DevChatter.Bot.Core.Events.Args; +using DevChatter.Bot.Core.Systems.Chat; +using System.Collections.Generic; + +namespace DevChatter.Bot.Core.Games.DealNoDeal +{ + /// + /// Picking starting boxes and the MainPlayer choosing a box are the same operation, + /// based on CHOSING_STARTING_BOXES or PICKING_BOXES the proper operations are performed + /// the OperandWords are the same !dnd pick x + /// + public class PickABoxOperation : BaseCommandOperation + { + private readonly DealNoDealGame _dealNoDealGame; + private readonly IChatClient _chatClient; + + + public PickABoxOperation(DealNoDealGame dealNoDealGame, IChatClient chatClient) + { + _chatClient = chatClient; + _dealNoDealGame = dealNoDealGame; + } + + public override List OperandWords { get; } = new List(){ "pick", "pcik", "pikc", "ipck" }; + public override string HelpText { get; } = + "Use \"!dnd pick x\" to pick/guess a box."; + + public override string TryToExecute(CommandReceivedEventArgs eventArgs) + { + if (!_dealNoDealGame.IsRunning) + { + return ""; + } + + bool isMainPlayer = _dealNoDealGame.MainPlayer != eventArgs.ChatUser; + string namePicked = eventArgs.Arguments[1]; + if (string.IsNullOrWhiteSpace(namePicked)) + { + return ""; + } + + if (_dealNoDealGame.GameState == DealNoDealGameState.ChosingStartingBoxes) + { + //Note: Only other players can choose a starting box + if (!isMainPlayer) + { + bool alreadyPickedABox = _dealNoDealGame.BoxesWithOwners.Exists(b => b.Owner == eventArgs.ChatUser.DisplayName); + if (alreadyPickedABox) + { + _chatClient.SendDirectMessage(eventArgs.ChatUser.DisplayName, "You already picked a box"); + return ""; + } + + Box chosenBox = _dealNoDealGame.StartingBoxes.Find(b => b.Id == int.Parse(namePicked)); + if (chosenBox == null) + { + _chatClient.SendDirectMessage(eventArgs.ChatUser.DisplayName,"That box number is not available, please chose a different number"); + return ""; + } + else + { + //AddBox + _chatClient.SendDirectMessage(eventArgs.ChatUser.DisplayName, $"Your box value is {_dealNoDealGame.GetBoxValue(chosenBox.TokenValue)}"); + chosenBox.SetOwner(eventArgs.ChatUser.DisplayName); + _dealNoDealGame.BoxesWithOwners.Add(chosenBox); + _dealNoDealGame.StartingBoxes.Remove(chosenBox); + } + } + } + if (_dealNoDealGame.GameState == DealNoDealGameState.PickingBoxes) + { + if (isMainPlayer) + { + //Pick a box + _dealNoDealGame.PickABox(namePicked, eventArgs.ChatUser.DisplayName); + } + } + return ""; + } + + } +} diff --git a/src/DevChatter.Bot.Infra.Twitch/TwitchChatClient.cs b/src/DevChatter.Bot.Infra.Twitch/TwitchChatClient.cs index 8cf858b0..e9de5a7a 100644 --- a/src/DevChatter.Bot.Infra.Twitch/TwitchChatClient.cs +++ b/src/DevChatter.Bot.Infra.Twitch/TwitchChatClient.cs @@ -36,7 +36,7 @@ public TwitchChatClient(TwitchClientSettings settings, _logger = logger; var credentials = new ConnectionCredentials(settings.TwitchUsername, settings.TwitchBotOAuth); _twitchClient = new TwitchClient(); - _twitchClient.Initialize(credentials, channel:settings.TwitchChannel); + _twitchClient.Initialize(credentials, channel: settings.TwitchChannel); _twitchClient.OnChatCommandReceived += ChatCommandReceived; _twitchClient.OnNewSubscriber += NewSubscriber; _twitchClient.OnUserJoined += TwitchClientOnOnUserJoined; @@ -74,6 +74,7 @@ public async Task Connect() private void TwitchClientConnected(object sender, OnConnectedArgs onConnectedArgs) { + _logger.LogInformation($"{nameof(TwitchChatClient)} connected"); _twitchClient.OnConnected -= TwitchClientConnected; diff --git a/src/DevChatter.Bot.Web/AutomatedActionSystem.cs b/src/DevChatter.Bot.Web/AutomatedActionSystem.cs index d6039a7c..c1fc582f 100644 --- a/src/DevChatter.Bot.Web/AutomatedActionSystem.cs +++ b/src/DevChatter.Bot.Web/AutomatedActionSystem.cs @@ -37,7 +37,8 @@ public void AddAction(IIntervalAction actionToAdd) RecurringJob.AddOrUpdate(action, Cron.MinuteInterval(currencyUpdate.IntervalInMinutes)); break; case DelayedMessageAction delayedMessageAction: - BackgroundJob.Schedule(action, delayedMessageAction.DelayTimeSpan); + BackgroundJob.Schedule(() => InvokeDelayedMessageAction(delayedMessageAction.Message), + delayedMessageAction.DelayTimeSpan); break; case OneTimeCallBackAction oneTimeCallBackAction: BackgroundJob.Schedule(action, oneTimeCallBackAction.DelayTimeSpan); @@ -49,6 +50,14 @@ public void AddAction(IIntervalAction actionToAdd) } } + public void InvokeDelayedMessageAction(string message) + { + foreach (IChatClient chatClient in _chatClients) + { + chatClient.SendMessage(message); + } + } + public void InvokeAutomatedMessage(string id) { var message = _repository.Single(DataItemPolicy.ById(Guid.Parse(id))); diff --git a/src/DevChatter.Bot.Web/Startup.cs b/src/DevChatter.Bot.Web/Startup.cs index ecbf3778..a2c7adec 100644 --- a/src/DevChatter.Bot.Web/Startup.cs +++ b/src/DevChatter.Bot.Web/Startup.cs @@ -8,6 +8,8 @@ using DevChatter.Bot.Core.Data; using DevChatter.Bot.Core.Data.Specifications; using DevChatter.Bot.Core.Events; +using DevChatter.Bot.Core.Games; +using DevChatter.Bot.Core.Games.DealNoDeal; using DevChatter.Bot.Core.Games.Hangman; using DevChatter.Bot.Core.Games.Heist; using DevChatter.Bot.Core.Games.Quiz; @@ -68,6 +70,9 @@ public void ConfigureServices(IServiceCollection services) IRepository repository = SetUpDatabase.SetUpRepository(fullConfig.DatabaseConnectionString); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(repository); services.AddSingleton();