using System.Collections.Generic;using System.Globalization; static readonly CultureInfo IC = CultureInfo.InvariantCulture; static readonly object gate = new object(); static volatile int lastLevel = -1; static Action<bool> hold; static DateTime nextState = DateTime.MinValue; static List<Preset> presets; static DateTime presetsAt; static volatile string shareSaid = "", shareStamp = ""; static volatile string checkDone = ""; public static void Wire(Bot bot, Settings s, string dir, Action<bool> holdFile) foreach (var old in new[] { "IdleMafiaBot.page", "IdleMafiaBot.ask", "IdleMafiaBot.answer" }) try { File.Delete(Path.Combine(dir, old)); } catch (Exception) { } bot.HeaderRead += h => { if (h != null && h.Level > 0) lastLevel = h.Level; }; lock (gate) { profile = p; profileOf = Profile.Key(p.Name); } checkDone = DateTime.UtcNow.Ticks.ToString(IC); nextState = DateTime.MinValue; bot.ProfileChanged += p => { lock (gate) if (profileOf == Profile.Key(p.Name)) profile = p; }; int at = line.IndexOf("Preset share: ", StringComparison.Ordinal); shareSaid = line.Substring(at + 14); shareStamp = DateTime.UtcNow.Ticks.ToString(IC); nextState = DateTime.MinValue; public static void Turn(Bot bot, Settings s, string dir) if (bot.Running) { if (bot.FightOnly || bot.RerollOnly) onlyRun = true; } else if (onlyRun) { onlyRun = false; if (hold != null) hold(true); } string ask = Path.Combine(dir, "IdleMafiaBot.ask"); if (File.Exists(ask)) Answer(bot, s, dir, ask); var now = DateTime.UtcNow; if (now >= nextState) { nextState = now.AddSeconds(2); WriteState(bot, s, dir); } static void Answer(Bot bot, Settings s, string dir, string file) try { text = File.ReadAllText(file); File.Delete(file); } catch (Exception) { return; } var part = text.Split(new[] { '\n' }, 3); string id = part[0].Trim(), what = part.Length > 1 ? part[1].Trim() : "", arg = part.Length > 2 ? part[2].TrimEnd('\r', '\n') : ""; string answer = Path.Combine(dir, "IdleMafiaBot.answer"); Action<string> reply = json => Write(answer, "{\"id\":" + Q(id) + "," + json + "}"); try { Do(bot, s, dir, what, arg, reply); } catch (Exception e) { reply(Said(false, "Something went wrong: " + e.Message)); } nextState = DateTime.MinValue; static void Do(Bot bot, Settings s, string dir, string what, string arg, Action<string> reply) reply(Said(true, "Working: only fighting players")); reply(Said(true, "Working: only rerolling the crew")); if (arg == "fight" && bot.FightOnly) bot.StopFightOnly(); else if (arg == "reroll" && bot.RerollOnly) bot.StopRerollOnly(); else { reply(Said(true, "")); return; } reply(Said(true, "Stopping")); if (arg == "card") MarkOffered(bot, s); if (bot.Running) { reply(Said(false, "Stop the bot first, then press Check my game.")); return; } reply(Said(true, "Checking")); if (profile == null) MarkOffered(bot, s); else if (lastLevel > 0) { s.CheckHintLevel = lastLevel; bot.SaveSettings(); } if (arg.Length > 0 && Enum.TryParse(arg, out f) && Enum.IsDefined(typeof(Focus), f) && f != s.Focus) { s.Focus = f; bot.SaveSettings(); } if (p == null) { reply(Said(false, "There's no check yet. Press Check my game.")); return; } var want = arg.Split(',').Select(x => x.Trim()).Where(x => x.Length > 0).ToList(); var chosen = Setup.Propose(p, s.Focus, s, bot.CatalogView).Where(pr => pr.Changes(s) && want.Contains(pr.Field)).ToList(); var said = chosen.Select(pr => pr.Name + " " + Setup.Show(pr.Field, pr.Value)).ToList(); foreach (var pr in chosen) typeof(Settings).GetField(pr.Field).SetValue(s, pr.Value); bot.Log(chosen.Count == 0 ? "Kept your settings after the game check" : "Set up from your game check (" + s.Focus + " focus): " + string.Join(", ", said)); reply(Said(true, "") + ",\"fields\":" + Arr(chosen.Select(pr => pr.Field))); bot.Log("Kept your settings after the game check"); reply(Said(true, "") + ",\"presets\":" + PresetList(bot, s, dir) + ",\"share\":" + B(bot.Remote.On)); string name = Preset.CleanName(arg); if (name.Length == 0) { reply(Said(false, "Type a name first.")); return; } var p = Preset.From(name, s); int old = list.FindIndex(o => Preset.SameName(o.Name, name)); if (old >= 0) list[old] = p; else list.Add(p); string why = SavePresets(bot, dir, list); if (why != null) { reply(Said(false, why)); return; } if (s.PresetInUse != p.Name) { s.PresetInUse = p.Name; bot.SaveSettings(); } bot.Log("Preset saved: " + p.Name); reply(Said(true, old >= 0 ? "\"" + p.Name + "\" now holds what's on." : "Saved \"" + p.Name + "\".") + ",\"presets\":" + PresetList(bot, s, dir)); var p = Presets(dir).FirstOrDefault(o => Preset.SameName(o.Name, arg)); if (p == null) { reply(Said(false, "No preset called \"" + arg + "\".")); return; } reply(Use(bot, s, dir, p)); var p = list.FirstOrDefault(o => Preset.SameName(o.Name, arg)); if (p == null) { reply(Said(false, "No preset called \"" + arg + "\".")); return; } string why = SavePresets(bot, dir, list); reply(Said(why == null, why ?? "\"" + p.Name + "\" deleted.") + ",\"presets\":" + PresetList(bot, s, dir)); var p = Presets(dir).FirstOrDefault(o => Preset.SameName(o.Name, arg)); if (p == null) { reply(Said(false, "No preset called \"" + arg + "\".")); return; } string why = bot.Remote.Share(p.Code()); if (why == "off") reply(Said(false, "To share with one click, switch on Stats in our Discord and Remote control from Discord (on this page, under Discord). Or press Copy code and type /preset share in our Discord.")); else if (why != null) reply(Said(false, "\"" + p.Name + "\" is too big to share this way. Press Copy code and type /preset share in our Discord.")); else { bot.Log("Preset: sharing " + p.Name + " in our Discord"); reply(Said(true, "Sharing \"" + p.Name + "\" in our Discord... (up to 15 seconds)")); } var p = Preset.Parse(arg, out why); if (p == null) { reply(Said(false, why)); return; } bool taken = Presets(dir).Any(o => Preset.SameName(o.Name, p.Name)); reply(Said(true, "") + ",\"preset\":" + PresetJson(p, null) + ",\"taken\":" + B(taken)); var p = Preset.Parse(arg, out why); if (p == null) { reply(Said(false, why)); return; } int old = list.FindIndex(o => Preset.SameName(o.Name, p.Name)); if (old >= 0) list[old] = p; else list.Add(p); why = SavePresets(bot, dir, list); if (why != null) { reply(Said(false, why)); return; } bot.Log("Preset added from a code: " + p.Name); if (what == "presetadduse") reply(Use(bot, s, dir, p)); else reply(Said(true, "Added \"" + p.Name + "\".") + ",\"presets\":" + PresetList(bot, s, dir)); bot.Discord.Test(arg, Discord.ShotOfRoblox, why => if (why.Length == 0) { reply(Said(true, "Sent. Look in your Discord channel.")); bot.Log("Discord: a test message sent"); } else reply(Said(false, why)); reply(Said(true, "") + ",\"text\":" + Q(bot.Stats.Preview())); reply(Payers(bot, Amount(s, arg))); long at = Amount(s, arg); var list = bot.Memory.BigPayers(at, DateTime.UtcNow); bot.Log("Fights: Big payers list saved (" + list.Count + " players)"); reply(Said(true, "") + ",\"text\":" + Q(PayersText(list, at, DateTime.UtcNow)) + ",\"file\":" + Q("Big payers " + DateTime.Now.ToString("dd-MM-yyyy HH.mm", IC) + ".txt")); if (bot.Memory.Forget(arg)) bot.Log("Fights: forgot " + arg + " (Big payers list)"); reply(Said(false, "The bot doesn't know that. Update it.")); static void WriteState(Bot bot, Settings s, string dir) string key = s.Account.Length > 0 ? Profile.Key(s.Account) : null; if (key != null && key != profileOf) { profileOf = key; profile = Profile.Load(Profile.FileFor(dir, s.Account)); } string when = p == null ? "Reads your game for about a minute and suggests settings. Nothing changes until you say so." : string.Format(IC, "Last check {0:dd-MM} at {0:HH:mm}{1}. Check again every few levels.", p.Checked, p.Level > 0 ? ", at level " + p.Level : ""); string title = null, card = null; if (of != null && !bot.Running) if (p == null && !Offered(s, of)) title = "Set the bot up for your account?"; card = "It reads your game for about a minute and suggests settings. Nothing changes until you say so."; else if (p != null && lastLevel > 0) card = Setup.RecheckHint(p, lastLevel, s.CheckHintLevel); title = "Check your game again?"; var now = Presets(dir).FirstOrDefault(x => x.Matches(s)); string only = bot.FightOnly ? "fight" : bot.RerollOnly ? "reroll" : ""; string json = "{\"only\":" + Q(only) + ",\"checking\":" + B(bot.Running && bot.CheckOrScan) + ",\"preset\":" + Q(now != null ? now.Name : "") + ",\"share\":" + B(bot.Remote.On) + ",\"shared\":" + Q(shareSaid) + ",\"sharedAt\":" + Q(shareStamp) + ",\"done\":" + Q(checkDone) + ",\"check\":{\"has\":" + B(p != null) + ",\"when\":" + Q(when) + ",\"title\":" + Q(card != null ? title : "") + ",\"text\":" + Q(card ?? "") + ",\"stamp\":" + Q(p != null ? p.Checked.ToString("yyyy-MM-dd HH:mm:ss", IC) : "") + "}}"; if (json == written) return; if (Write(Path.Combine(dir, "IdleMafiaBot.page"), json)) written = json; static bool Offered(Settings s, string key) { return s.CheckOffered.Split(',').Any(n => n.Trim() == key); } static void MarkOffered(Bot bot, Settings s) lock (gate) of = profileOf; if (of == null || Offered(s, of)) return; s.CheckOffered = s.CheckOffered.Trim().Length == 0 ? of : s.CheckOffered.Trim() + "," + of; static string Review(Bot bot, Settings s) if (p == null) return Said(false, "There's no check yet. Press Check my game."); string sub = (p.Name.Length > 0 ? p.Name : "Your account") + (p.Level > 0 ? ", level " + p.Level : "") + ", checked " + p.Checked.ToString("dd-MM", IC) + " at " + p.Checked.ToString("HH:mm", IC); var found = Setup.Found(p).Select(r => Arr(r)); var changes = new List<string>(); var kept = new List<string>(); foreach (var pr in Setup.Propose(p, s.Focus, s, bot.CatalogView)) string own = Setup.OwnChoice(pr, s); string row = "{\"field\":" + Q(pr.Field) + ",\"name\":" + Q(pr.Name) + ",\"old\":" + Q(Setup.Show(pr.Field, pr.Current(s))) + ",\"new\":" + Q(Setup.Show(pr.Field, pr.Value)) + ",\"reason\":" + Q(pr.Reason) + ",\"note\":" + Q(own ?? "") + ",\"use\":" + B(own == null) + "}"; (pr.Changes(s) ? changes : kept).Add(row); return Said(true, "") + ",\"sub\":" + Q(sub) + ",\"focus\":" + Q(s.Focus.ToString()) + ",\"focusHint\":" + Q(Setup.FocusHint(s.Focus)) + ",\"found\":[" + string.Join(",", found) + "],\"changes\":[" + string.Join(",", changes) + "],\"kept\":[" + string.Join(",", kept) + "]"; static List<Preset> Presets(string dir) try { at = File.Exists(Preset.FileIn(dir)) ? File.GetLastWriteTimeUtc(Preset.FileIn(dir)) : DateTime.MinValue; } catch (Exception) { at = DateTime.MinValue; } if (presets == null || at != presetsAt) { presets = Preset.Load(dir); presetsAt = at; } static string SavePresets(Bot bot, string dir, List<Preset> list) try { Preset.Save(dir, list); presets = null; bot.Remote.Wake(); return null; } catch (Exception e) { presets = null; bot.Log("Couldn't save the presets: " + e.Message); return "Couldn't save the presets: " + e.Message; } static string Use(Bot bot, Settings s, string dir, Preset p) int skipped = p.ApplyTo(s); bot.Log("Preset: using " + p.Name + (skipped > 0 ? " (" + skipped + " settings left as they were)" : "")); return Said(true, "Using \"" + p.Name + "\"." + (skipped > 0 ? " " + skipped + " of its settings didn't fit this version and stay as they were." : "")) + ",\"preset\":" + PresetJson(p, null) + ",\"presets\":" + PresetList(bot, s, dir); static string PresetList(Bot bot, Settings s, string dir) return "[" + string.Join(",", Presets(dir).Select(p => PresetJson(p, s))) + "]"; static string PresetJson(Preset p, Settings s) var sb = new StringBuilder("{\"name\":").Append(Q(p.Name)).Append(",\"code\":").Append(Q(p.Code())).Append(",\"newer\":").Append(p.Newer.ToString(IC)); sb.Append(",\"on\":").Append(B(on)).Append(",\"exact\":").Append(B(p.Matches(s, true))) .Append(",\"changed\":").Append(B(!on && Preset.SameName(p.Name, s.PresetInUse))); sb.Append(",\"values\":{").Append(string.Join(",", p.Values.Select(v => Q(v.Key) + ":" + Q(v.Value)))).Append('}'); sb.Append(",\"missing\":").Append(Arr(Preset.Fields().Where(f => p.Value(f.Name) == null).Select(f => f.Name))).Append('}'); static long Amount(Settings s, string arg) return long.TryParse(arg, NumberStyles.Integer, IC, out v) && v > 0 ? v : s.BigPayerCash; static string Payers(Bot bot, long at) var now = DateTime.UtcNow; var rows = m.BigPayers(at, now).Select(p => "{\"name\":" + Q(p.Name) + ",\"biggest\":" + Q(SafehouseInfo.Money(p.Biggest)) + ",\"last\":" + Q(p.Last >= 0 ? SafehouseInfo.Money(p.Last) : "-") + ",\"when\":" + Q(Ago(p.LastBig, now)) + ",\"record\":" + Q(p.Won + "W " + p.Lost + "L") + ",\"tip\":" + Q(p.Fights + (p.Fights == 1 ? " fight" : " fights") + " in the last 3 days (the last 10 are kept): " + p.Won + " won, " + p.Lost + " lost") + "}"); return Said(true, "") + ",\"rows\":[" + string.Join(",", rows) + "],\"read\":" + Q(n == 0 ? "" : n + (n == 1 ? " player" : " players") + " remembered") + ",\"respect\":" + Q("Player fights written down: " + m.RespectSummary(now)); static string PayersText(List<FightMemory.Payer> list, long at, DateTime now) var t = new StringBuilder(); t.AppendLine("Big payers: paid " + SafehouseInfo.Money(at) + " or more in a win, the last 3 days"); t.AppendLine("Saved " + DateTime.Now.ToString("dd-MM-yyyy HH:mm", IC) + " by Idle Mafia Bot"); if (list.Count == 0) t.AppendLine("No player has paid that much in the last 3 days."); int w = Math.Max(6, list.Max(p => p.Name.Length)) + 2; t.AppendLine("PLAYER".PadRight(w) + "BIGGEST".PadLeft(10) + "LAST".PadLeft(10) + "LAST BIG ONE".PadLeft(16) + "FIGHTS".PadLeft(10)); t.AppendLine(p.Name.PadRight(w) + SafehouseInfo.Money(p.Biggest).PadLeft(10) + (p.Last >= 0 ? SafehouseInfo.Money(p.Last) : "-").PadLeft(10) + Ago(p.LastBig, now).PadLeft(16) + (p.Won + "W " + p.Lost + "L").PadLeft(10)); static string Ago(DateTime utc, DateTime nowUtc) if (d.TotalMinutes < 1) return "just now"; if (d.TotalMinutes < 60) return (int)d.TotalMinutes + " min ago"; if (d.TotalHours < 24) return (int)d.TotalHours + " h ago"; return utc.ToLocalTime().ToString("dd-MM HH:mm", IC); static string Said(bool ok, string say) { return "\"ok\":" + B(ok) + ",\"say\":" + Q(say); } static string B(bool b) { return b ? "true" : "false"; } static string Arr(IEnumerable<string> items) { return "[" + string.Join(",", items.Select(Q)) + "]"; } static string Q(string t) var sb = new StringBuilder("\""); foreach (char c in t ?? "") if (c == '"') sb.Append("\\\""); else if (c == '\\') sb.Append("\\\\"); else if (c == '\n') sb.Append("\\n"); else if (c == '\r') sb.Append("\\r"); else if (c == '\t') sb.Append("\\t"); else if (c < ' ' || c == (char)0x2028 || c == (char)0x2029) sb.Append("\\u").Append(((int)c).ToString("x4", IC)); return sb.Append('"').ToString(); static bool Write(string file, string text) string tmp = file + "." + Thread.CurrentThread.ManagedThreadId.ToString(IC) + ".tmp"; File.WriteAllText(tmp, text, new UTF8Encoding(false)); if (File.Exists(file)) File.Delete(file); try { File.Delete(tmp); } catch (Exception) { }