private static void Main(string[] args) { try { if (args.Length == 1) { try { SaveFingerprint(Guid.Parse(args[0])); Console.WriteLine("Fingerprint is saved, now you can relauch bot with needed parameters"); } catch { Console.WriteLine("You should pass correct 128-bit fingerprint (GUID)"); } return; } ushort width, height; PlacingOrderMode order = PlacingOrderMode.Random; notificationMode = CaptchaNotificationMode.Browser; try { try { leftX = short.Parse(args[0]); topY = short.Parse(args[1]); fingerprint = GetFingerprint(); string logFilePath = null; if (args.Length > 3) { notificationMode = CaptchaNotificationMode.None; string upper = args[3].ToUpper(); if (upper.Contains('B')) { notificationMode |= CaptchaNotificationMode.Browser; } if (upper.Contains('S')) { notificationMode |= CaptchaNotificationMode.Sound; } if (args.Length > 4) { defendMode = args[4].ToUpper() == "Y"; if (args.Length > 5) { switch (args[5].ToUpper()) { case "R": order = PlacingOrderMode.FromRight; break; case "L": order = PlacingOrderMode.FromLeft; break; case "T": order = PlacingOrderMode.FromTop; break; case "B": order = PlacingOrderMode.FromBottom; break; } if (args.Length > 6) { try { File.OpenWrite(args[6]).Dispose(); logFilePath = args[6]; } catch { } } } } } logger = new Logger(finishCTS.Token, logFilePath); imagePixels = ImageProcessing.PixelColorsByUri(args[2], logger.LogLine); checked { width = (ushort)imagePixels.GetLength(0); height = (ushort)imagePixels.GetLength(1); short check; check = (short)(leftX + width); check = (short)(topY + height); } } catch (OverflowException) { throw new Exception("Entire image should be inside the map"); } catch (WebException) { throw new Exception("Cannot download image"); } catch (ArgumentException) { throw new Exception("Cannot convert image"); } catch (IOException) { throw new Exception("Fingerprint is not saved, pass it from browser as only parameter to app to save before usage"); } catch { throw new Exception("Parameters: <leftX> <topY> <imageURI> [notificationMode: N/B/S/BS = B] [defendMode: Y/N = N] [buildFrom L/R/T/B/RND = RND] [logFileName = none]"); } } catch (Exception ex) { Console.WriteLine(ex.Message); finishCTS.Cancel(); return; } IEnumerable <int> allY = Enumerable.Range(0, height); IEnumerable <int> allX = Enumerable.Range(0, width); Pixel[] nonEmptyPixels = allX. SelectMany(X => allY.Select(Y => (X: (short)(X + leftX), Y: (short)(Y + topY), C: imagePixels[X, Y]))). Where(xy => xy.C != PixelColor.None).ToArray(); switch (order) { case PlacingOrderMode.FromLeft: pixelsToBuild = nonEmptyPixels.OrderBy(xy => xy.Item1).ToList(); break; case PlacingOrderMode.FromRight: pixelsToBuild = nonEmptyPixels.OrderByDescending(xy => xy.Item1).ToList(); break; case PlacingOrderMode.FromTop: pixelsToBuild = nonEmptyPixels.OrderBy(xy => xy.Item2).ToList(); break; case PlacingOrderMode.FromBottom: pixelsToBuild = nonEmptyPixels.OrderByDescending(xy => xy.Item2).ToList(); break; default: Random rnd = new Random(); for (int i = 0; i < nonEmptyPixels.Length; i++) { int r = rnd.Next(i, nonEmptyPixels.Length); Pixel tmp = nonEmptyPixels[r]; nonEmptyPixels[r] = nonEmptyPixels[i]; nonEmptyPixels[i] = tmp; } pixelsToBuild = nonEmptyPixels; break; } cache = new ChunkCache(pixelsToBuild, logger.LogLine); mapDownloadedResetEvent = new ManualResetEvent(false); cache.OnMapDownloaded += (o, e) => mapDownloadedResetEvent.Set(); if (defendMode) { gotGriefed = new AutoResetEvent(false); cache.OnMapDownloaded += (o, e) => gotGriefed.Set(); waitingGriefLock = new object(); } statsThread = new Thread(StatsCollectionThreadBody); statsThread.Start(); do { try { using (InteractionWrapper wrapper = new InteractionWrapper(fingerprint, logger.LogLine)) { wrapper.OnPixelChanged += LogPixelChanged; wrapper.OnConnectionLost += (o, e) => mapDownloadedResetEvent.Reset(); cache.Wrapper = wrapper; cache.DownloadChunks(); placed.Clear(); bool wasChanged; do { wasChanged = false; repeatingFails = false; foreach (Pixel pixel in pixelsToBuild) { (short x, short y, PixelColor color) = pixel; PixelColor actualColor = cache.GetPixelColor(x, y); if (!IsCorrectPixelColor(actualColor, color)) { wasChanged = true; bool success; placed.Add(pixel); do { byte placingPixelFails = 0; mapDownloadedResetEvent.WaitOne(); success = wrapper.PlacePixel(x, y, color, out double cd, out double totalCd, out string error); if (success) { string prefix = cd == 4 ? "P" : "Rep"; logger.LogPixel($"{prefix}laced pixel:", MessageGroup.Pixel, x, y, color); Thread.Sleep(TimeSpan.FromSeconds(totalCd < 53 ? 1 : cd)); } else { if (cd == 0.0) { logger.LogLine("Please go to browser and place pixel, then return and press any key", MessageGroup.Captcha); if (notificationMode.HasFlag(CaptchaNotificationMode.Sound)) { Task.Run(() => { for (int j = 0; j < 7; j++) { Console.Beep(1000, 100); } }); } if (notificationMode.HasFlag(CaptchaNotificationMode.Browser)) { Process.Start($"{InteractionWrapper.BaseHttpAdress}/#{x},{y},30"); } Thread.Sleep(100); logger.ConsoleLoggingResetEvent.Reset(); while (Console.KeyAvailable) { Console.ReadKey(true); } Console.ReadKey(true); logger.ConsoleLoggingResetEvent.Set(); } else { logger.LogLine($"Failed to place pixel: {error}", MessageGroup.PixelFail); if (++placingPixelFails == 3) { throw new Exception("Cannot place pixel 3 times"); } } Thread.Sleep(TimeSpan.FromSeconds(cd)); } } while (!success); } } if (defendMode) { if (!wasChanged) { logger.LogLine("Image is intact, waiting...", MessageGroup.ImageDone); lock (waitingGriefLock) { gotGriefed.Reset(); gotGriefed.WaitOne(); } Thread.Sleep(new Random().Next(500, 3000)); } } }while (defendMode || wasChanged); logger.LogLine("Building is finished, exiting...", MessageGroup.ImageDone); return; } } catch (Exception ex) { logger.LogLine($"Unhandled exception: {ex.Message}", MessageGroup.Error); int delay = repeatingFails ? 30 : 10; repeatingFails = true; logger.LogLine($"Reconnecting in {delay} seconds...", MessageGroup.TechState); Thread.Sleep(TimeSpan.FromSeconds(delay)); continue; } } while (true); } finally { finishCTS.Cancel(); gotGriefed?.Dispose(); mapDownloadedResetEvent?.Dispose(); logger?.Dispose(); Thread.Sleep(1000); finishCTS.Dispose(); if (statsThread?.IsAlive ?? false) { statsThread.Interrupt(); //fallback, should never work } } }