private void DBAddBluff(CPlayer p, string cardUsed, bool v = false, CPlayer caller = null) { using (var db = new CoupContext()) { var gp = GetDBGamePlayer(p, db); var calledgp = GetDBGamePlayer(caller, db); var dbp = GetDBPlayer(p, db); int?calledBy = GetDBPlayer(caller, db)?.Id; if (!v) { gp.BluffsMade++; } else { calledgp.BluffsCalled++; } var b = new Bluff { BluffCalled = v, BlufferId = dbp.Id, CardBluffed = cardUsed, CalledById = calledBy, GameId = DBGameId }; db.Bluffs.Add(b); db.SaveChanges(); } }
public Game(int id, User u, bool group, bool random, Chat c = null) { GameId = id; ChatId = c?.Id ?? 0; IsGroup = group; if (IsGroup) { using (var db = new CoupContext()) { var grp = db.ChatGroups.FirstOrDefault(x => x.TelegramId == c.Id); if (grp == null) { grp = new ChatGroup { TelegramId = c.Id, Created = DateTime.UtcNow, Language = "English", Name = c.Title, Username = c.Username }; db.ChatGroups.Add(grp); } grp.Name = c.Title; grp.Username = c.Username; db.SaveChanges(); } } IsRandom = random; AddPlayer(u); new Task(JoinTimer).Start(); }
static void Main(string[] args) { AppDomain.CurrentDomain.UnhandledException += (sender, eventArgs) => { //drop the error to log file and exit using (var sw = new StreamWriter(Path.Combine(Bot.RootDirectory, "Logs\\error.log"), true)) { var e = (eventArgs.ExceptionObject as Exception); sw.WriteLine(DateTime.Now); sw.WriteLine(e.Message); sw.WriteLine(e.StackTrace + "\n"); if (eventArgs.IsTerminating) { Environment.Exit(5); } } }; //initialize EF before we start receiving using (var db = new CoupContext()) { var count = db.ChatGroups.Count(); } new Thread(Bot.Initialize).Start(); new Thread(Cleaner).Start(); new Thread(Monitor).Start(); Thread.Sleep(-1); }
public static bool BetaCheck(this User u) { using (var db = new CoupContext()) { var p = db.Players.FirstOrDefault(x => x.TelegramId == u.Id); if (p == null && db.Players.Count() < 700) { p = new Player { Created = DateTime.UtcNow, Language = "English", Name = (u.FirstName + " " + u.LastName).Trim(), TelegramId = u.Id, Username = u.Username }; db.Players.Add(p); db.SaveChanges(); return(true); } else if (p != null) { return(true); } return(false); } }
private Player GetDBPlayer(CPlayer player, CoupContext db) { if (player == null) { return(null); } if (player.DBPlayerId == 0) { var p = db.Players.FirstOrDefault(x => x.TelegramId == player.Id); player.DBPlayerId = p?.Id ?? 0; return(p); } try { return(db.Players.Find(player.DBPlayerId)); } catch { return(null); } }
private GamePlayer GetDBGamePlayer(CPlayer player, CoupContext db) { if (player == null) { return(null); } if (player.DBGamePlayerId == 0) { var p = GetDBGamePlayer(GetDBPlayer(player, db)); player.DBGamePlayerId = p?.Id ?? 0; return(p); } try { return(db.GamePlayers.Find(player.DBGamePlayerId)); } catch { return(null); } }
private void DBAddValue(CPlayer p, Models.ValueType prop, int val = 1) { new Task(() => { using (var db = new CoupContext()) { var gp = GetDBGamePlayer(p, db); switch (prop) { case Models.ValueType.CoinsCollected: gp.CoinsCollected += val; break; case Models.ValueType.CoinsStolen: gp.CoinsStolen += val; break; case Models.ValueType.ActionsBlocked: gp.ActionsBlocked += val; break; case Models.ValueType.BluffsCalled: throw new Exception("Use DBAddBluff"); case Models.ValueType.PlayersCouped: gp.PlayersCouped += val; break; case Models.ValueType.PlayersAssassinated: gp.PlayersAssassinated += val; break; } db.SaveChanges(); } }).Start(); }
public void TellCards(CPlayer p) { if (p.Cards.Count() == 0) { return; } var cards = p?.Cards.Aggregate("<b>Your cards</b>", (cur, b) => cur + $"\n{b.Name}"); if (!p.HasCheckedCards) { using (var db = new CoupContext()) { var dbp = db.Players.FirstOrDefault(x => x.TelegramId == p.Id); var gp = dbp?.GamePlayers.FirstOrDefault(x => x.GameId == DBGameId); if (gp != null) { gp.LookedAtCardsTurn = Math.Max(Round, 1); db.SaveChanges(); } } p.HasCheckedCards = true; } Send(cards, p.Id, newMsg: true).ToList(); }
public void StartGame() { if (State != GameState.Joining) { return; } LastMenu = null; Send("Game is starting!"); if (Players.Count < 2) { Send("Not enough players to start the game.."); State = GameState.Ended; return; } if (State != GameState.Joining) { return; } Program.GamesPlayed++; State = GameState.Initializing; Players.Shuffle(); Players.Shuffle(); //create db entry for game using (var db = new CoupContext()) { var grp = db.ChatGroups.FirstOrDefault(x => x.TelegramId == ChatId); var g = new Database.Game { GroupId = grp?.Id, GameType = IsGroup ? "Group" : IsRandom ? "Stranger" : "Friend", TimeStarted = DateTime.Now }; db.Games.Add(g); db.SaveChanges(); DBGameId = g.Id; } //hand out cards foreach (var p in Players) { var card = Cards.First(); Cards.Remove(card); p.Cards.Add(card); } //round robin :P foreach (var p in Players) { var card = Cards.First(); Cards.Remove(card); p.Cards.Add(card); using (var db = new CoupContext()) { //create db entry var dbp = db.Players.FirstOrDefault(x => x.TelegramId == p.Id); if (dbp == null) { dbp = new Player { TelegramId = p.Id, Created = DateTime.UtcNow, Language = "English", Name = p.Name, Username = p.TeleUser.Username }; db.Players.Add(dbp); db.SaveChanges(); } dbp.Name = p.Name; dbp.Username = p.TeleUser.Username; p.Language = dbp.Language; var gp = new GamePlayer() { GameId = DBGameId, StartingCards = p.Cards.Aggregate("", (a, b) => a + "," + b.Name) }; dbp.GamePlayers.Add(gp); db.SaveChanges(); } //tell player their cards SendMenu(p); //TellCards(p); } //DEBUG OUT #if DEBUG //for (int i = 0; i < Players.Count(); i++) //{ // Console.WriteLine($"Player {i}: {Players[i].Cards[0].Name}, {Players[i].Cards[1].Name}"); //} //Console.WriteLine($"Deck:\n{Cards.Aggregate("", (a, b) => a + "\n" + b.Name)}"); #endif State = GameState.Running; while (true) { try { Round++; //will use break to end the game foreach (var p in Players) { Cards.Shuffle(); if (p.Cards.Count() == 0) { continue; } Turn = p.Id; Send($"{p.GetName()}'s turn. {p.Name.ToBold()} has {p.Coins} coins.", newMsg: true).ToList(); p.CallBluff = false; //it is the players turn. //first off, do they have to Coup? if (p.Coins >= 10) { //Coup time! Send($"{p.GetName()} has 10 or more coins, and must Coup someone."); ChoiceMade = Action.Coup; } else { //ok, no, so get actions that they can make Send($"{p.Name.ToBold()} please choose an action.", p.Id, menu: CreateActionMenu(p), menuTo: p.Id, newMsg: true); LastMenu = null; if (IsGroup) { Send($"{p.Name.ToBold()} please choose an action."); } } var choice = WaitForChoice(ChoiceType.Action); Console.WriteLine($"{p.Name} has chosen to {ChoiceMade}"); CPlayer target; CPlayer blocker; CPlayer bluffer; IEnumerable <CPlayer> choices; switch (choice) { //DONE case Action.Income: LastMenu = null; Send($"{p.Name.ToBold()} has chosen to take income (1 coin)."); DBAddValue(p, Models.ValueType.CoinsCollected); p.Coins++; break; case Action.ForeignAid: if (PlayerMadeBlockableAction(p, choice, cardUsed: "Ambassador")) { LastMenu = null; Send($"{p.Name.ToBold()} was not blocked, and has gained two coins."); DBAddValue(p, Models.ValueType.CoinsCollected, 2); p.Coins += 2; } break; case Action.Coup: p.Coins -= 7; Send($"{p.Name.ToBold()} has chosen to Coup."); choices = Players.Where(x => x.Id != p.Id && x.Cards.Count() > 0); if (choices.Count() == 1) { target = choices.First(); } else { Send($"Please choose who to Coup.", p.Id, menu: CreateTargetMenu(p), menuTo: p.Id); LastMenu = null; WaitForChoice(ChoiceType.Target); target = Players.FirstOrDefault(x => x.Id == ChoiceTarget); } DBAddValue(p, Models.ValueType.PlayersCouped); if (target == null) { LastMenu = null; Send($"{p.Name.ToBold()} did not choose in time, and has lost 7 coins"); Send("Times up!", p.Id, menuTo: p.Id); break; } if (target.Cards.Count() == 1) { PlayerLoseCard(target, target.Cards.First()); //Graveyard.Add(target.Cards.First()); target.Cards.Clear(); LastMenu = null; Send($"{target.Name.ToBold()}, you have been couped. You are out of cards, and therefore out of the game!"); } else { Send($"{p.Name.ToBold()} has chosen to Coup {target.Name.ToBold()}! {target.Name.ToBold()} please choose a card to lose."); PlayerLoseCard(target); } break; case Action.Tax: if (PlayerMadeBlockableAction(p, Action.Tax)) { LastMenu = null; Send($"{p.Name.ToBold()} was not blocked, and has gained three coins."); DBAddValue(p, Models.ValueType.CoinsCollected, 3); p.Coins += 3; } break; case Action.Assassinate: //OH BOY p.Coins -= 3; Send($"{p.Name.ToBold()} has paid 3 coins to assassinate."); choices = Players.Where(x => x.Id != p.Id && x.Cards.Count() > 0); if (choices.Count() == 1) { target = choices.First(); } else { Send($"Please choose who to assassinate.", p.Id, menu: CreateTargetMenu(p), menuTo: p.Id); LastMenu = null; WaitForChoice(ChoiceType.Target); target = Players.FirstOrDefault(x => x.Id == ChoiceTarget); } if (target == null) { LastMenu = null; Send("Times up!", p.Id, menuTo: p.Id); Send($"{p.Name.ToBold()} did not choose in time, and has lost 3 coins!"); break; } if (PlayerMadeBlockableAction(p, choice, target, "Assassin")) { DBAddValue(p, Models.ValueType.PlayersAssassinated); //unblocked! if (target.Cards.Count() == 1) { Send($"{target.Name.ToBold()}, you have been assassinated. You are out of cards, and therefore out of the game!"); PlayerLoseCard(target, target.Cards.First()); //Graveyard.Add(target.Cards.First()); target.Cards.Clear(); } else { Send($"{p.Name.ToBold()} was not blocked! {target.Name.ToBold()} please choose a card to lose."); PlayerLoseCard(target); } } break; case Action.Exchange: if (PlayerMadeBlockableAction(p, choice)) { LastMenu = null; Cards.Shuffle(); var count = p.Cards.Count(); var newCards = Cards.Take(count).ToList(); Cards.AddRange(p.Cards); newCards.AddRange(p.Cards.ToList()); p.Cards.Clear(); var menu = new InlineKeyboardMarkup(new[] { newCards.Select(x => new InlineKeyboardButton(x.Name, $"card|{GameId}|{x.Name}")).ToArray() }); LastMenu = null; Turn = p.Id; //just to be sure Send($"{p.Name.ToBold()} was not blocked. Please choose your new cards in PM @{Bot.Me.Username}."); Send($"{p.Name}, choose your first card", p.Id, menu: menu, newMsg: true); WaitForChoice(ChoiceType.Card); if (String.IsNullOrEmpty(CardToLose)) { LastMenu = null; Send($"Time ran out, moving on"); p.Cards.AddRange(newCards.Take(count)); TellCards(p); break; } var card2 = ""; newCards.Remove(newCards.First(x => x.Name == CardToLose)); var card1 = CardToLose; CardToLose = null; if (count == 2) { menu = new InlineKeyboardMarkup(new[] { newCards.Select(x => new InlineKeyboardButton(x.Name, $"card|{GameId}|{x.Name}")).ToArray() }); Send($"{p.Name}, choose your second card", p.Id, menu: menu, menuTo: p.Id); WaitForChoice(ChoiceType.Card); if (String.IsNullOrEmpty(CardToLose)) { LastMenu = null; Send($"Time ran out, moving on"); p.Cards.Add(newCards.First()); TellCards(p); break; } card2 = CardToLose; CardToLose = null; newCards.Remove(newCards.First(x => x.Name == card2)); } var newCard = Cards.First(x => x.Name == card1); Cards.Remove(newCard); p.Cards.Add(newCard); if (count == 2) { newCard = Cards.First(x => x.Name == card2); Cards.Remove(newCard); p.Cards.Add(newCard); } Cards.Shuffle(); TellCards(p); } break; case Action.Steal: Send($"{p.Name.ToBold()} has chosen to steal."); choices = Players.Where(x => x.Id != p.Id && x.Cards.Count() > 0); if (choices.Count() == 1) { target = choices.First(); } else { Send($"{p.Name.ToBold()} please choose who to steal from.", p.Id, menu: CreateTargetMenu(p), menuTo: p.Id); LastMenu = null; WaitForChoice(ChoiceType.Target); target = Players.FirstOrDefault(x => x.Id == ChoiceTarget); } if (target == null) { LastMenu = null; Send("Times up!", p.Id, menuTo: p.Id); Send($"{p.Name.ToBold()} did not choose in time"); break; } if (PlayerMadeBlockableAction(p, choice, target, "Captain")) { LastMenu = null; var coinsTaken = Math.Min(target.Coins, 2); DBAddValue(p, Models.ValueType.CoinsStolen, coinsTaken); p.Coins += coinsTaken; target.Coins -= coinsTaken; Send($"{p.Name.ToBold()} was not blocked, and has taken {coinsTaken} coins from {target.Name.ToBold()}."); } break; case Action.Concede: Graveyard.AddRange(p.Cards); p.Cards.Clear(); LastMenu = null; Send($"{p.Name} has chosen to concede the game, and is out!"); break; default: LastMenu = null; Send($"{p.Name.ToBold()} did not do anything, moving on..."); p.AfkCount++; if (p.AfkCount >= 2) { //out! Graveyard.AddRange(p.Cards); p.Cards.Clear(); Send($"{p.Name} is AFK, and is out!"); } break; } //reset all the things ChoiceMade = Action.None; ChoiceTarget = 0; CardToLose = ""; foreach (var pl in Players) { pl.CallBluff = null; pl.Block = null; } if (Players.Count(x => x.Cards.Count() > 0) == 1) { //game is over var winner = Players.FirstOrDefault(x => x.Cards.Count() > 0); //set the winner in the database using (var db = new CoupContext()) { var gp = GetDBGamePlayer(winner, db); gp.Won = true; gp.EndingCards = winner.Cards.Count() > 1 ? winner.Cards.Aggregate("", (a, b) => a + "," + b.Name) : winner.Cards.First().Name; var g = db.Games.Find(DBGameId); g.TimeEnded = DateTime.UtcNow; db.SaveChanges(); } LastMenu = null; Send($"{winner.GetName()} has won the game!", newMsg: true); State = GameState.Ended; return; } } } catch (Exception e) { Send($"Game error: {e.Message}\n{e.StackTrace}"); State = GameState.Ended; return; } } }
internal static void HandleCallback(CallbackQuery c) { try { //https://telegram.me/coup2bot?startgroup=gameid //https://telegram.me/coup2bot?start=gameid var cmd = c.Data; if (cmd.Contains("|")) { cmd = cmd.Split('|')[0]; } Game g; int id; CPlayer p; Models.Action a; if (Enum.TryParse(cmd, out a)) { id = int.Parse(c.Data.Split('|')[1]); g = Program.Games.FirstOrDefault(x => x.GameId == id); if (g != null) { if (g.Turn == c.From.Id) { g.ChoiceMade = a; Bot.ReplyToCallback(c, $"Choice accepted: {cmd}"); } else { Bot.ReplyToCallback(c, "It's not your turn!", false, true); } } else { Bot.ReplyToCallback(c, $"That game isn't active anymore..."); } } switch (cmd) { case "players": id = int.Parse(c.Data.Split('|')[1]); g = Program.Games.FirstOrDefault(x => x.GameId == id); if (g == null) { Bot.ReplyToCallback(c, "Game is no longer active!"); break; } //player wants status var msg = g.Players.Where(x => x.Cards.Count > 0).Aggregate("Cards - Coins - Name", (cur, b) => cur + $"\n{b.Cards.Count} - {(b.Coins > 9 ? b.Coins.ToString() : "0" + b.Coins)} - {b.Name}"); Bot.ReplyToCallback(c, msg, false, true); break; case "grave": id = int.Parse(c.Data.Split('|')[1]); g = Program.Games.FirstOrDefault(x => x.GameId == id); if (g == null) { Bot.ReplyToCallback(c, "Game is no longer active!"); break; } //player wants graveyard var grave = g.Graveyard.Aggregate("Graveyard", (cur, b) => cur + $"\n{b.Name}"); Bot.ReplyToCallback(c, grave, false, true); break; case "cards": id = int.Parse(c.Data.Split('|')[1]); g = Program.Games.FirstOrDefault(x => x.GameId == id); if (g == null) { Bot.ReplyToCallback(c, "Game is no longer active!"); break; } //player wants graveyard var player = g.Players.FirstOrDefault(x => x.Id == c.From.Id); var cards = player?.Cards.Aggregate("Your cards", (cur, b) => cur + $"\n{b.Name}"); if (!player.HasCheckedCards) { using (var db = new CoupContext()) { var dbp = db.Players.FirstOrDefault(x => x.TelegramId == c.From.Id); var gp = dbp?.GamePlayers.FirstOrDefault(x => x.GameId == g.DBGameId); if (gp != null) { gp.LookedAtCardsTurn = Math.Max(g.Round, 1); db.SaveChanges(); } } player.HasCheckedCards = true; } Bot.ReplyToCallback(c, cards, false, true); break; case "concede": id = int.Parse(c.Data.Split('|')[1]); g = Program.Games.FirstOrDefault(x => x.GameId == id); if (g == null) { Bot.ReplyToCallback(c, "Game is no longer active!"); break; } if (g.Turn != c.From.Id) { Bot.ReplyToCallback(c, "Please concede on your turn only", false, true); break; } g.Concede(); Bot.ReplyToCallback(c, "Accepted"); break; case "card": //player is losing a card var cardStr = c.Data.Split('|')[2]; id = int.Parse(c.Data.Split('|')[1]); g = Program.Games.FirstOrDefault(x => x.GameId == id); if (g != null) { p = g.Players.FirstOrDefault(x => x.Id == c.From.Id); if (p != null && p.Id == g.Turn) { g.CardToLose = cardStr; Bot.ReplyToCallback(c, "Choice Accepted - " + cardStr); } } break; case "bluff": bool call = c.Data.Split('|')[1] == "call"; id = int.Parse(c.Data.Split('|')[2]); g = Program.Games.FirstOrDefault(x => x.GameId == id); if (g != null) { //get the player p = g.Players.FirstOrDefault(x => x.Id == c.From.Id); if (p != null && p.Cards.Count() > 0) { if (p.Id == g.Turn) { Bot.ReplyToCallback(c, "You can't block yourself!", false, true); } else { switch (c.Data.Split('|')[1]) { case "call": p.CallBluff = true; p.Block = false; break; case "allow": p.Block = p.CallBluff = false; break; case "block": if (p.Block == false) { Bot.ReplyToCallback(c, "You are not allowed to block", false, true); break; } p.Block = true; p.CallBluff = false; break; } Bot.ReplyToCallback(c, "Choice accepted", false, false); } } else { Bot.ReplyToCallback(c, "You aren't in the game!", false, true); } } break; case "choose": //picking a target var target = int.Parse(c.Data.Split('|')[2]); id = int.Parse(c.Data.Split('|')[1]); g = Program.Games.FirstOrDefault(x => x.GameId == id); if (g != null) { //get the player p = g.Players.FirstOrDefault(x => x.Id == c.From.Id); if (p != null && p.Cards.Count() > 0) { if (g.Turn != c.From.Id) { Bot.ReplyToCallback(c, "It's not your choice!", false, true); } else { g.ChoiceTarget = target; Bot.ReplyToCallback(c, "Choice accepted"); } } else { Bot.ReplyToCallback(c, "You aren't in the game!", false, true); } } break; //TODO: change these to enums with int values case "spgf": g = CreateGame(c.From); var menu = new InlineKeyboardMarkup(new[] { new InlineKeyboardButton("Start", "start|" + g.GameId.ToString()) }); Bot.ReplyToCallback(c, $"Great! I've created a game for you. Share this link to invite friends: https://telegram.me/{Bot.Me.Username}?start={g.GameId}", replyMarkup: menu); break; case "spgs": //check for a game waiting for more players if (Program.Games.Any(x => x.Players.Any(pl => pl.Id == c.From.Id))) { Bot.ReplyToCallback(c, "You are already in a game!"); } g = Program.Games.FirstOrDefault(x => x.State == GameState.Joining && x.Players.Count() < 6 & !x.IsGroup && x.IsRandom); if (g != null) { var result = g.AddPlayer(c.From); switch (result) { case 1: Bot.ReplyToCallback(c, "You are already in the game!"); break; case 0: Bot.ReplyToCallback(c, "You have joined the game!"); break; } //TODO: give player list, total count //Bot.ReplyToCallback(c, "You have joined a game!"); } else { g = CreateGame(c.From, false, true); Bot.ReplyToCallback(c, $"There were no games available, so I have created a new game for you. Please wait for others to join!", replyMarkup: new InlineKeyboardMarkup(new[] { new InlineKeyboardButton("Start Game", $"start|{g.GameId}") })); } //Console.WriteLine($"{c.From.FirstName} has joined game: {g.GameId}"); break; case "sgg": if (Program.Games.Any(x => x.Players.Any(pl => pl.Id == c.From.Id))) { Bot.ReplyToCallback(c, "You are already in a game!"); } g = CreateGame(c.From, true); Bot.ReplyToCallback(c, $"Great! I've created a game for you. Click below to send the game to the group!", replyMarkup: new InlineKeyboardMarkup(new[] { new InlineKeyboardButton("Click here") { Url = $"https://telegram.me/{Bot.Me.Username}?startgroup={g.GameId}" } })); break; case "join": if (!c.From.BetaCheck()) { Bot.ReplyToCallback(c, "Sorry, but beta testing is full. Please wait until the next beta extension.", false, true); return; } id = int.Parse(c.Data.Split('|')[1]); g = Program.Games.FirstOrDefault(x => x.GameId == id); if (Program.Games.Any(x => x.Players.Any(pl => pl.Id == c.From.Id))) { Bot.ReplyToCallback(c, "You are already in a game!", false, true); break; } if (g != null) { var success = g.AddPlayer(c.From); switch (success) { case 0: Bot.ReplyToCallback(c, "You have joined a game!", false, true); break; case 1: Bot.ReplyToCallback(c, "You are already in the game!", false, true); break; } } break; case "start": id = int.Parse(c.Data.Split('|')[1]); g = Program.Games.FirstOrDefault(x => x.GameId == id); if (g == null) { break; } if (!g.Players.Any(x => x.Id == c.From.Id)) { Bot.ReplyToCallback(c, "You aren't even in the game!", false, true); return; } if (g.Players.First().Id != c.From.Id) { Bot.ReplyToCallback(c, "Only game creator can start the game", false, true); return; } g?.StartGame(); break; } } catch (AggregateException e) { Bot.ReplyToCallback(c, $"Error: {e.InnerExceptions[0].Message}"); } catch (Exception e) { Bot.ReplyToCallback(c, $"Error: {e.Message}\n{e.StackTrace}"); } }
internal static void HandleMessage(Message m) { try { if (m.Date < Program.StartTime.AddSeconds(-5)) { return; } //start@coup2bot gameid //get the command if any var cmd = m.Text.Split(' ')[0]; if (!cmd.StartsWith("!") & !cmd.StartsWith("/")) { return; } cmd = cmd.Replace("@" + Bot.Me.Username, "").Replace("!", "").Replace("/", "").ToLower(); if (String.IsNullOrEmpty(cmd)) { return; } Game g; //TODO - Move all of these to Commands using reflection //basic commands switch (cmd) { case "addbeta": if (m.From.Id != Bot.Para) { return; } //adds the player to the beta var u = m.ReplyToMessage?.From; if (u == null) { Bot.SendAsync("Reply to a user to add them to the beta", m.Chat.Id); return; } using (var db = new CoupContext()) { var p = db.Players.FirstOrDefault(x => x.TelegramId == u.Id); if (p != null) { Bot.SendAsync("User is already in the beta!", m.Chat.Id); return; } p = new Player { Created = DateTime.UtcNow, Language = "English", Name = (u.FirstName + " " + u.LastName).Trim(), TelegramId = u.Id, Username = u.Username }; db.Players.Add(p); db.SaveChanges(); Bot.SendAsync($"{p.Name} has been added to Coup Beta", m.Chat.Id); } break; case "removeuseless": if (m.From.Id != Bot.Para) { return; } using (var db = new CoupContext()) { var delete = db.Players.FirstOrDefault(x => x.TelegramId == 0); var dropPlayers = db.Players.Where(x => x.GamePlayers.Count <= 1); var count = dropPlayers.Count(); var gps = dropPlayers.SelectMany(x => x.GamePlayers).Select(x => x.Id).ToList(); var dropGp = db.GamePlayers.Where(x => gps.Contains(x.Id)); foreach (var d in dropGp) { d.PlayerId = delete.Id; } foreach (var p in dropPlayers) { db.Players.Remove(p); } db.SaveChanges(); Bot.SendAsync($"Removed {count} players from beta", m.Chat.Id); } break; case "stats": var stats = $"<b>Stats for {m.From.FirstName.FormatHTML()}</b>\n"; using (var db = new CoupContext()) { var p = db.Players.FirstOrDefault(x => x.TelegramId == m.From.Id); if (p == null) { return; } var gps = p.GamePlayers.ToList(); stats += $"Games played: {gps.Count()}\n" + $"Games won: {gps.Count(x => x.Won)}\n" + $"Checks cards on turn: {gps.Average(x => x.LookedAtCardsTurn)}\n" + $"Total coins collected: {gps.Sum(x => x.CoinsCollected)}\n" + $"Total coins stolen: {gps.Sum(x => x.CoinsStolen)}\n" + $"Successful blocks: {gps.Sum(x => x.ActionsBlocked)}\n" + $"Successful bluffs: {gps.Sum(x => x.BluffsMade)}\n" + $"Bluffs called: {gps.Sum(x => x.BluffsCalled)}\n" + $"Coups made: {gps.Sum(x => x.PlayersCouped)}\n" + $"Assassinations: {gps.Sum(x => x.PlayersAssassinated)}"; Bot.SendAsync(stats, m.Chat.Id); } break; case "help": Bot.SendAsync("https://www.youtube.com/watch?v=xUNWl5fWfEY", m.Chat.Id); break; case "ping": Ping(m); break; case "start": if (!m.From.BetaCheck()) { Bot.Api.SendTextMessageAsync(m.Chat.Id, "Sorry, but beta testing is full. Please wait until the next beta extension.", replyToMessageId: m.MessageId); return; } Bot.SendAsync("During the beta, we ask that players join the beta feedback group: https://telegram.me/joinchat/B7EXdEE_fl3Jmsi7TL02_A", m.Chat.Id); //check for gameid //Console.WriteLine(m.From.FirstName + ": " + m.From.Username + ": " + m.From.Id); try { var id = int.Parse(m.Text.Split(' ')[1]); var startgame = Program.Games.FirstOrDefault(x => x.GameId == id); if (startgame != null) { if (Program.Games.Any(x => x.Players.Any(p => p.Id == m.From.Id))) { Bot.SendAsync("You are already in a game!", m.From.Id); return; } //check if group or PM if (m.Chat.Type == Telegram.Bot.Types.Enums.ChatType.Private) { //private chat - was it sent by the actual person? if (startgame.Players.Any(x => x.Id == m.From.Id)) { //already in game return; } startgame.AddPlayer(m.From); } else if (m.Chat.Type != Telegram.Bot.Types.Enums.ChatType.Channel) { startgame.ChatId = m.Chat.Id; var menu = new InlineKeyboardMarkup(new[] { new InlineKeyboardButton("Join", "join|" + id.ToString()), new InlineKeyboardButton("Start", "start|" + id.ToString()) }); var r = Bot.SendAsync(m.From.FirstName + " wants to play coup. Up to 6 players total can join. Click below to join the game!", m.Chat.Id, customMenu: menu ).Result; startgame.LastMessageId = r.MessageId; startgame.LastMessageSent = r.Text; startgame.LastMenu = menu; } } } catch { //no game / parameter Bot.SendAsync("Well hi there! Would you like to play a game?\r\nHow about a nice game of tic tac toe?\r\n....Global Thermonuclear War it is. Just kidding! Use /newgame to play a game of Coup with your friends, or complete strangers!", m.From.Id); } break; case "maint": if (m.From.Id == Bot.Para) { Bot.Maintenance = true; Bot.SendAsync("Maintenance mode enabled, no new games", m.Chat.Id); } break; case "shutdown": if (m.From.Id == Bot.Para) { Bot.Maintenance = true; var msg = "We've had to kill the game for a moment to patch a critical issue. Please use /newgame in a moment to start a new game.\nSorry for the inconvenience."; foreach (var game in Program.Games.ToList()) { if (game.IsGroup) { Bot.SendAsync(msg, game.ChatId); } else { foreach (var p in game.Players.ToList()) { Bot.SendAsync(msg, p.Id); Thread.Sleep(500); } } Thread.Sleep(500); } Bot.SendAsync("Bot killed", m.Chat.Id); Thread.Sleep(1000); Environment.Exit(0); } break; case "newgame": if (Bot.Maintenance) { Bot.SendAsync("Cannot start game, bot is about to restart for patching", m.Chat.Id); return; } if (!m.From.BetaCheck()) { Bot.Api.SendTextMessageAsync(m.Chat.Id, "Sorry, but beta testing is full. Please wait until the next beta extension.", replyToMessageId: m.MessageId); return; } if (m.Chat.Id == -1001094680157) { Bot.SendAsync("This group is for feedback. Please join https://telegram.me/joinchat/B7EXdEDhVB6A6RL6Mi3Hvw to play, or play in PM", m.Chat.Id); return; } Bot.SendAsync("During the beta, we ask that players join the beta feedback group: https://telegram.me/joinchat/B7EXdEE_fl3Jmsi7TL02_A", m.Chat.Id); //Console.WriteLine(m.From.FirstName + ": " + m.From.Username + ": " + m.From.Id); //check to see if an existing game is already being played. // if group, just look for a group game with the chat id // if PM, look for a game with the user as one of the players (alive) if (!UserCanStartGame(m.From.Id, m.Chat.Id)) { return; } //all is good? Ask if PM or Group game (if in PM, otherwise assume group) if (m.Chat.Type == Telegram.Bot.Types.Enums.ChatType.Private) { Bot.Api.SendTextMessageAsync(m.Chat.Id, "You've chosen to start a new game. Do you want to play in private with friends, private with random players, or in a group?", replyMarkup: new InlineKeyboardMarkup(new InlineKeyboardButton[][] { new InlineKeyboardButton[] { new InlineKeyboardButton("Private game - Friends", "spgf") }, new InlineKeyboardButton[] { new InlineKeyboardButton("Private game - Strangers", "spgs") }, //new InlineKeyboardButton[] { new InlineKeyboardButton("Group game", "sgg") } })); Thread.Sleep(500); Bot.SendAsync("Alternatively, you can start a game directly in a group", m.Chat.Id); } else { //group game g = CreateGame(m.From, true, c: m.Chat); var menu = new InlineKeyboardMarkup(new[] { new InlineKeyboardButton("Join", "join|" + g.GameId.ToString()), new InlineKeyboardButton("Start", "start|" + g.GameId.ToString()) }); var r = Bot.SendAsync(m.From.FirstName + " wants to play coup. Up to 6 players total can join. Click below to join the game!", m.Chat.Id, customMenu: menu ).Result; g.LastMessageId = r.MessageId; g.LastMessageSent = r.Text; g.LastMenu = menu; } break; case "leave": //find the game g = Program.Games.FirstOrDefault(x => x.Players.Any(p => p.Id == m.From.Id)); var rem = g?.RemovePlayer(m.From); if (rem == 1) { Bot.Api.SendTextMessageAsync(m.From.Id, $"You have been removed from game {g.GameId}"); } break; case "test": break; } } catch (AggregateException e) { Bot.SendAsync($"Error: {e.InnerExceptions[0].Message}", m.Chat.Id); } catch (Exception e) { Bot.SendAsync($"Error: {e.Message}\n{e.StackTrace}", m.Chat.Id); } }