Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
kgsensei
GitHub Repository: kgsensei/NitroTypeHack2
Path: blob/main/src/Controller.cs
3446 views
using Microsoft.Web.WebView2.Core;
using Microsoft.Web.WebView2.WinForms;

namespace NitroType3 {
    class Controller {
        private static readonly Random RandGen = new();

        // Simulation of typing text, this is the star of
        // the show and where most of the hardcore logic
        // for the cheat is stored
        public async static Task SimulateTypingText(string Text, WebView2 webView) {
            Config.CheatRunning = true;
            Logger.Log("Func SimulateTypingText Running");

            int TypingRate = CalculateVariancy(Config.TypingRate, Config.TypingRateVariancy, Min: 10);
            int Accuracy = CalculateVariancy(Config.Accuracy, Config.AccuracyVariancy, Max: 100);

            Logger.Log($"Calculated Race Specific Typing Rate: {TypingRate}wpm");
            Logger.Log($"Calculated Race Specific Accuracy: {Accuracy}%");

            if (Config.GodMode) Accuracy = 100;

            if (Config.UseNitros && !Config.GodMode) {
                string[] Words = Text.Split('\u00A0');
                Words = ReplaceLongest(Words, "ʜ");
                Text = string.Join(" ", Words);
            }

            char[] InvalidCharacters = { '`', '~', '@', '#', '$', '%', '^', '&', '*', '(', ')' };
            char[] Letters = Text.Replace("\u00A0", " ").ToCharArray();

            int MissIndex = 0;

            int LettersToMiss = (int)Math.Floor(Letters.Length * ((decimal)(100 - Accuracy) / 100));
            int MaxIndex = (int)Math.Floor(Letters.Length / (decimal)(LettersToMiss + 1));

            double[] sinTable = [.. Enumerable.Range(0, 256).Select(x => Math.Sin(x))];
            List<string> commandTable = [];

            // Prebuild commands and put them in a table
            for (int i = 0; i < Letters.Length; i++) {
                char Letter = Letters[i];
                if (Letter == 'ʜ') {
                    commandTable.Add(@"{""type"": ""rawKeyDown"", ""windowsVirtualKeyCode"": 13, ""unmodifiedText"": ""\r"", ""text"": ""\r""}");
                    commandTable.Add(@"{""type"": ""keyUp"", ""windowsVirtualKeyCode"": 13, ""unmodifiedText"": ""\r"", ""text"": ""\r""}");
                } else if (Letter == 32) {
                    commandTable.Add(@"{ ""type"": ""char"", ""keyIdentifier"": ""U+0020"", ""text"": "" "", ""code"": ""Space"", ""key"": "" "" }");
                } else {
                    commandTable.Add(BuildCharEvent(Letter));
                }
                if (MissIndex == MaxIndex) {
                    commandTable.Add(BuildCharEvent(InvalidCharacters.Sample()));
                    MissIndex = 0;
                } else {
                    MissIndex++;
                }
            }

            // Iterate over the already computed commands and dispatch them to the webview
            for (int i = 0; i < commandTable.Count; i++) {
                await Dispatch(webView, commandTable[i]);
                if (Config.GodMode) {
                    // For some reason removing this causes it to trip up, but waiting
                    // zero seconds fixes it?? I imagine this is because of how it handles
                    // await events or smth, maybe it's compensating for the browsers
                    // interpret duration...
                    await Task.Delay(0);
                } else {
                    await Task.Delay((int)(sinTable[i & 255] * 10 + TypingRate));
                }
            }

            // If auto game is turned on then wait a short period and press enter
            if (Config.AutoGame) {
                await Task.Delay(RandGen.Next(4250, 4500));
                await SendEnter(webView);
            }

            Logger.Log("Finished Typing");
            Config.CheatRunning = false;
        }

        private static async Task Dispatch(WebView2 webView, string json) => await webView.CoreWebView2.CallDevToolsProtocolMethodAsync("Input.dispatchKeyEvent", json).ConfigureAwait(false);
        private static string BuildCharEvent(char c) => $@"{{ ""type"": ""char"", ""text"": ""{GetEscapeSequence(c)}"" }}";
        private static async Task SendEnter(WebView2 webView) {
            await Dispatch(webView, @"{""type"": ""rawKeyDown"", ""windowsVirtualKeyCode"": 13, ""unmodifiedText"": ""\r"", ""text"": ""\r""}");
            await Dispatch(webView, @"{""type"": ""keyUp"", ""windowsVirtualKeyCode"": 13, ""unmodifiedText"": ""\r"", ""text"": ""\r""}");
        }
        private static string GetEscapeSequence(char c) => "\\u" + ((int)c).ToString("X4");

        /**
         * @param int       Base amount to change
         * @param int       + or - amount to change
         * @param int       Maximum value
         * @param int       Minimum value
         * @returns int     Fully modified base value
         */
        private static int CalculateVariancy(int Base, int Variancy, int Max = 0, int Min = 0) {
            Base += RandGen.Next(-Variancy, Variancy);
            if (Max != 0 && Base > Max) Base = Max;
            if (Min != 0 && Base < Min) Base = Min;

            return Base;
        }

        /**
         * @param string[]      Words String array to search from
         * @param string        NewValue New value to replace longest item in string array
         * @returns string[]    Original string array with longest value replaced
         */
        private static string[] ReplaceLongest(string[] Words, string NewValue) {
            int Length = 0;
            int Index = 0;

            for (int i = 0; i < Words.Length; i++) {
                if (Words[i].Length > Length) {
                    Length = Words[i].Length;
                    Index = i;
                }
            }

            Words[Index] = NewValue;

            return Words;
        }
    }

    // Adds .sample() as a method to arrays because
    // I refuse to type all that out just to get a
    // random element from an array, utter bullshit
    public static class ArrayExtension {
        public static char Sample(this char[] array) {
            return array[ Random.Shared.Next(0, array.Length - 1) ];
        }
    }
}