AFK Macro

Idle Mafia Macro/Source/src/linux/LinuxPage.cs

LinuxPage.cs

Linux: the settings page · 448 lines · 23 KB

using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
namespace IdleMafiaBot
{
static class LinuxPage
{
static readonly CultureInfo IC = CultureInfo.InvariantCulture;
static readonly object gate = new object();
static Profile profile;
static string profileOf;
static volatile int lastLevel = -1;
static bool onlyRun;
static Action<bool> hold;
static string written;
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)
{
hold = 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; };
bot.ProfileRead += p =>
{
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; };
bot.Logged += line =>
{
int at = line.IndexOf("Preset share: ", StringComparison.Ordinal);
if (at < 0) return;
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)
{
string text;
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)
{
switch (what)
{
case "fightonly":
bot.StartFightOnly();
reply(Said(true, "Working: only fighting players"));
return;
case "rerollonly":
bot.StartRerollOnly();
reply(Said(true, "Working: only rerolling the crew"));
return;
case "onlystop":
if (arg == "fight" && bot.FightOnly) bot.StopFightOnly();
else if (arg == "reroll" && bot.RerollOnly) bot.StopRerollOnly();
else { reply(Said(true, "")); return; }
reply(Said(true, "Stopping"));
return;
case "check":
if (arg == "card") MarkOffered(bot, s);
if (bot.Running) { reply(Said(false, "Stop the bot first, then press Check my game.")); return; }
bot.RequestCheck();
reply(Said(true, "Checking"));
return;
case "checklater":
lock (gate)
{
if (profile == null) MarkOffered(bot, s);
else if (lastLevel > 0) { s.CheckHintLevel = lastLevel; bot.SaveSettings(); }
}
reply(Said(true, ""));
return;
case "review":
{
Focus f;
if (arg.Length > 0 && Enum.TryParse(arg, out f) && Enum.IsDefined(typeof(Focus), f) && f != s.Focus) { s.Focus = f; bot.SaveSettings(); }
reply(Review(bot, s));
return;
}
case "usecheck":
{
Profile p;
lock (gate) p = profile;
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.SaveSettings();
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)));
return;
}
case "keepcheck":
bot.Log("Kept your settings after the game check");
reply(Said(true, ""));
return;
case "presets":
reply(Said(true, "") + ",\"presets\":" + PresetList(bot, s, dir) + ",\"share\":" + B(bot.Remote.On));
return;
case "presetsave":
case "presetupdate":
{
string name = Preset.CleanName(arg);
if (name.Length == 0) { reply(Said(false, "Type a name first.")); return; }
var list = Presets(dir);
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));
return;
}
case "presetuse":
{
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));
return;
}
case "presetdelete":
{
var list = Presets(dir);
var p = list.FirstOrDefault(o => Preset.SameName(o.Name, arg));
if (p == null) { reply(Said(false, "No preset called \"" + arg + "\".")); return; }
list.Remove(p);
string why = SavePresets(bot, dir, list);
reply(Said(why == null, why ?? "\"" + p.Name + "\" deleted.") + ",\"presets\":" + PresetList(bot, s, dir));
return;
}
case "presetshare":
{
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)")); }
return;
}
case "presetcheck":
{
string why;
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));
return;
}
case "presetadd":
case "presetadduse":
{
string why;
var p = Preset.Parse(arg, out why);
if (p == null) { reply(Said(false, why)); return; }
var list = Presets(dir);
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));
return;
}
case "discordtest":
bot.Discord.Test(arg, Discord.ShotOfRoblox, why =>
{
try
{
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));
}
catch (Exception) { }
});
return;
case "statspreview":
reply(Said(true, "") + ",\"text\":" + Q(bot.Stats.Preview()));
return;
case "payers":
reply(Payers(bot, Amount(s, arg)));
return;
case "payersfile":
{
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"));
return;
}
case "payerforget":
if (bot.Memory.Forget(arg)) bot.Log("Fights: forgot " + arg + " (Big payers list)");
reply(Said(true, ""));
return;
}
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;
Profile p;
string of;
lock (gate)
{
if (key != null && key != profileOf) { profileOf = key; profile = Profile.Load(Profile.FileFor(dir, s.Account)); }
p = profile;
of = profileOf;
}
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)
{
string of;
lock (gate) of = profileOf;
if (of == null || Offered(s, of)) return;
s.CheckOffered = s.CheckOffered.Trim().Length == 0 ? of : s.CheckOffered.Trim() + "," + of;
bot.SaveSettings();
}
static string Review(Bot bot, Settings s)
{
Profile p;
lock (gate) p = profile;
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)
{
DateTime at;
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; }
return presets;
}
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);
s.PresetInUse = p.Name;
bot.SaveSettings();
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));
if (s != null)
{
bool on = p.Matches(s);
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('}');
return sb.ToString();
}
static long Amount(Settings s, string arg)
{
long v;
return long.TryParse(arg, NumberStyles.Integer, IC, out v) && v > 0 ? v : s.BigPayerCash;
}
static string Payers(Bot bot, long at)
{
var m = bot.Memory;
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") + "}");
int n = m.Count;
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");
t.AppendLine();
if (list.Count == 0) t.AppendLine("No player has paid that much in the last 3 days.");
else
{
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));
foreach (var p in list)
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));
}
return t.ToString();
}
static string Ago(DateTime utc, DateTime nowUtc)
{
var d = nowUtc - utc;
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));
else sb.Append(c);
}
return sb.Append('"').ToString();
}
static bool Write(string file, string text)
{
string tmp = file + "." + Thread.CurrentThread.ManagedThreadId.ToString(IC) + ".tmp";
try
{
File.WriteAllText(tmp, text, new UTF8Encoding(false));
if (File.Exists(file)) File.Delete(file);
File.Move(tmp, file);
return true;
}
catch (Exception)
{
try { File.Delete(tmp); } catch (Exception) { }
return false;
}
}
}
}