/// <summary> /// Replaces variables recursively, expanding lists or dictionaries with jolly indices. /// </summary> public static List <string> ReplaceValuesRecursive(string original, LSGlobals ls) { var data = ls.BotData; var globals = ls.Globals; var toReplace = new List <string>(); // Regex parse the syntax <LIST[*]> var matches = Regex.Matches(original, @"<([^\[]*)\[\*\]>"); var variables = new List <ListOfStringsVariable>(); foreach (Match m in matches) { var name = m.Groups[1].Value; // Retrieve the variable var variable = GetVariables(data).Get(name); // If it's null, try to get it from the global variables if (variable == null) { variable = globals.Get(name); // If still null, there's nothing to replace, skip it if (variable == null) { continue; } } // Make sure it's a List of strings and add it to the list if (variable is ListOfStringsVariable list) { variables.Add(list); } } // If there's no corresponding variable, just readd the input string and proceed with normal replacement if (variables.Count > 0) { // Example: we have 3 lists of sizes 3, 7 and 5. We need to take 7 var max = variables.Max(v => v.AsListOfStrings().Count); for (var i = 0; i < max; i++) { var replaced = original; foreach (var variable in variables) { var list = variable.AsListOfStrings(); replaced = list.Count > i?replaced.Replace($"<{variable.Name}[*]>", list[i]) : replaced.Replace($"<{variable.Name}[*]>", "NULL"); } toReplace.Add(replaced); } goto END; } // Regex parse the syntax <DICT(*)> (wildcard key -> returns list of all values) var match = Regex.Match(original, @"<([^\(]*)\(\*\)>"); if (match.Success) { var full = match.Groups[0].Value; var name = match.Groups[1].Value; // Retrieve the dictionary var dict = GetVariables(data).Get <DictionaryOfStringsVariable>(name); if (dict == null) { dict = globals.Get <DictionaryOfStringsVariable>(name); } // If there's no corresponding variable, just readd the input string and proceed with normal replacement if (dict == null) { toReplace.Add(original); } else { foreach (var item in dict.AsDictionaryOfStrings()) { toReplace.Add(original.Replace(full, item.Value)); } } goto END; } // Regex parse the syntax <DICT{*}> (wildcard value -> returns list of all keys) match = Regex.Match(original, @"<([^\{]*)\{\*\}>"); if (match.Success) { var full = match.Groups[0].Value; var name = match.Groups[1].Value; // Retrieve the dictionary var dict = GetVariables(data).Get <DictionaryOfStringsVariable>(name); if (dict == null) { if (name == "COOKIES") { dict = new DictionaryOfStringsVariable(data.COOKIES) { Name = name }; } else if (name == "HEADERS") { dict = new DictionaryOfStringsVariable(data.HEADERS) { Name = name }; } else { dict = globals.Get <DictionaryOfStringsVariable>(name); } } // If there's no corresponding variable, just readd the input string and proceed with normal replacement if (dict == null) { toReplace.Add(original); } else { foreach (var item in dict.AsDictionaryOfStrings()) { toReplace.Add(original.Replace(full, item.Key)); } } goto END; } // If no other match was a success, it means there's no recursive value and we simply add the input to the list toReplace.Add(original); END: // Now for each item in the list, do the normal replacement and return the replaced list of strings return(toReplace.Select(i => (string)ReplaceValues(i, ls)).ToList()); }
/// <inheritdoc /> public override async Task Process(LSGlobals ls) { var data = ls.BotData; await base.Process(ls); var replacedInput = ReplaceValues(InputString, ls); var variablesList = GetVariables(data); Variable variableToAdd = null; var logColor = IsCapture ? LogColors.Tomato : LogColors.Yellow; switch (Group) { case UtilityGroup.List: var list = variablesList.Get <ListOfStringsVariable>(ListName)?.AsListOfStrings(); var list2 = variablesList.Get <ListOfStringsVariable>(SecondListName)?.AsListOfStrings(); var item = ReplaceValues(ListItem, ls); var index = int.Parse(ReplaceValues(ListIndex, ls)); switch (ListAction) { case ListAction.Create: variableToAdd = new ListOfStringsVariable(new List <string>()); break; case ListAction.Length: variableToAdd = new StringVariable(list.Count.ToString()); break; case ListAction.Join: variableToAdd = new StringVariable(string.Join(Separator, list)); break; case ListAction.Sort: var sorted = list.Select(e => e).ToList(); // Clone the list so we don't edit the original one if (Numeric) { var nums = sorted.Select(e => double.Parse(e, CultureInfo.InvariantCulture)).ToList(); nums.Sort(); sorted = nums.Select(e => e.ToString()).ToList(); } else { sorted.Sort(); } if (!Ascending) { sorted.Reverse(); } variableToAdd = new ListOfStringsVariable(sorted); break; case ListAction.Concat: variableToAdd = new ListOfStringsVariable(list.Concat(list2).ToList()); break; case ListAction.Zip: variableToAdd = new ListOfStringsVariable(list.Zip(list2, (a, b) => a + b).ToList()); break; case ListAction.Map: variableToAdd = new DictionaryOfStringsVariable(list.Zip(list2, (k, v) => new { k, v }).ToDictionary(x => x.k, x => x.v)); break; case ListAction.Add: // Handle negative indices index = index switch { 0 => 0, < 0 => index + list.Count, _ => index }; list.Insert(index, item); break; case ListAction.Remove: // Handle negative indices index = index switch { 0 => 0, < 0 => index + list.Count, _ => index }; list.RemoveAt(index); break; case ListAction.RemoveValues: variableToAdd = new ListOfStringsVariable(list.Where(l => !Condition.Verify(new KeycheckCondition { Left = ReplaceValues(l, ls), Comparer = ListElementComparer, Right = ListComparisonTerm })).ToList()); break; case ListAction.RemoveDuplicates: variableToAdd = new ListOfStringsVariable(list.Distinct().ToList()); break; case ListAction.Random: variableToAdd = new StringVariable(list[data.Random.Next(list.Count)]); break; case ListAction.Shuffle: // This makes a copy of the original list var listCopy = new List <string>(list); listCopy.Shuffle(data.Random); variableToAdd = new ListOfStringsVariable(listCopy); break; default: break; } data.Logger.Log($"Executed action {ListAction} on list {ListName}", logColor); break; case UtilityGroup.Variable: string single = variablesList.Get <StringVariable>(VarName).AsString(); switch (VarAction) { case VarAction.Split: variableToAdd = new ListOfStringsVariable(single.Split(new string[] { ReplaceValues(SplitSeparator, ls) }, StringSplitOptions.None).ToList()); break; } data.Logger.Log($"Executed action {VarAction} on variable {VarName}", logColor); break; case UtilityGroup.Conversion: var conversionInputBytes = replacedInput.ConvertFrom(ConversionFrom); var conversionResult = conversionInputBytes.ConvertTo(ConversionTo); variableToAdd = new StringVariable(conversionResult); data.Logger.Log($"Executed conversion {ConversionFrom} to {ConversionTo} on input {replacedInput} with outcome {conversionResult}", logColor); break; case UtilityGroup.File: var file = ReplaceValues(FilePath, ls); FileUtils.ThrowIfNotInCWD(file); switch (FileAction) { case FileAction.Exists: variableToAdd = new StringVariable(File.Exists(file).ToString()); break; case FileAction.Read: lock (FileLocker.GetHandle(file)) variableToAdd = new StringVariable(File.ReadAllText(file)); break; case FileAction.ReadLines: lock (FileLocker.GetHandle(file)) variableToAdd = new ListOfStringsVariable(File.ReadAllLines(file).ToList()); break; case FileAction.Write: FileUtils.CreatePath(file); lock (FileLocker.GetHandle(file)) File.WriteAllText(file, replacedInput.Unescape()); break; case FileAction.WriteLines: FileUtils.CreatePath(file); lock (FileLocker.GetHandle(file)) File.WriteAllLines(file, ReplaceValuesRecursive(InputString, ls).Select(i => i.Unescape())); break; case FileAction.Append: FileUtils.CreatePath(file); lock (FileLocker.GetHandle(file)) File.AppendAllText(file, replacedInput.Unescape()); break; case FileAction.AppendLines: FileUtils.CreatePath(file); lock (FileLocker.GetHandle(file)) File.AppendAllLines(file, ReplaceValuesRecursive(InputString, ls).Select(i => i.Unescape())); break; case FileAction.Copy: var fileCopyLocation = ReplaceValues(InputString, ls); FileUtils.ThrowIfNotInCWD(fileCopyLocation); FileUtils.CreatePath(fileCopyLocation); lock (FileLocker.GetHandle(file)) lock (FileLocker.GetHandle(fileCopyLocation)) File.Copy(file, fileCopyLocation); break; case FileAction.Move: var fileMoveLocation = ReplaceValues(InputString, ls); FileUtils.ThrowIfNotInCWD(fileMoveLocation); FileUtils.CreatePath(fileMoveLocation); lock (FileLocker.GetHandle(file)) lock (FileLocker.GetHandle(fileMoveLocation)) File.Move(file, fileMoveLocation); break; case FileAction.Delete: // No deletion if the file is in use (DB/OpenBullet.db cannot be deleted but instead DB/OpenBullet-BackupCopy.db) // If another process is just reading the file it will be deleted lock (FileLocker.GetHandle(file)) File.Delete(file); break; } data.Logger.Log($"Executed action {FileAction} on file {file}", logColor); break; case UtilityGroup.Folder: var folder = ReplaceValues(FolderPath, ls); FileUtils.ThrowIfNotInCWD(folder); switch (FolderAction) { case FolderAction.Exists: variableToAdd = new StringVariable(Directory.Exists(folder).ToString()); break; case FolderAction.Create: variableToAdd = new StringVariable(Directory.CreateDirectory(folder).ToString()); break; case FolderAction.Delete: Directory.Delete(folder, true); break; } data.Logger.Log($"Executed action {FolderAction} on folder {folder}", logColor); break; default: break; } if (variableToAdd is not null) { variableToAdd.Name = VariableName; variableToAdd.MarkedForCapture = IsCapture; variablesList.Set(variableToAdd); } }
/// <summary> /// Replaces variables in a given input string. /// </summary> public static string ReplaceValues(string original, LSGlobals ls) { if (original == null) { return(string.Empty); } var data = ls.BotData; var globals = ls.Globals; if (!original.Contains("<") && !original.Contains(">")) { return(original); } var previous = ""; var output = original; do { previous = output; // Replace all the fixed quantities output = output.Replace("<INPUT>", data.Line.Data); output = output.Replace("<STATUS>", data.STATUS); output = output.Replace("<SOURCE>", data.SOURCE); output = output.Replace("<COOKIES>", data.COOKIES.AsString()); output = output.Replace("<HEADERS>", data.HEADERS.AsString()); output = output.Replace("<RESPONSECODE>", data.RESPONSECODE.AsString()); output = output.Replace("<ADDRESS>", data.ADDRESS); output = output.Replace("<RETRIES>", data.Line.Retries.ToString()); var lastCaptchaInfo = data.TryGetObject <CaptchaInfo>("lastCaptchaInfo"); if (lastCaptchaInfo is not null) { output = output.Replace("<CAPTCHAID>", lastCaptchaInfo.Id.ToString()); } // TODO: Readd this // output = output.Replace("<BOTNUM>", data.BotNumber.ToString()); if (data.Proxy != null) { output = output.Replace("<PROXY>", data.Proxy.ToString()); } // Get all the inner (max. 1 level of nesting) variables var matches = Regex.Matches(output, @"<([^<>]*)>"); foreach (Match match in matches) { var full = match.Groups[0].Value; var m = match.Groups[1].Value; // Parse the variable name var name = Regex.Match(m, @"^[^\[\{\(]*").Value; // Try to get the variable (first local, then global, then if none was found go to the next iteration) // We don't throw an error here because it could be some HTML or XML code e.g. <br> that triggers this, and we dont' want to spam the user with unneeded errors var v = GetVariables(data).Get(name); if (v == null) { if (name == "COOKIES") { v = new DictionaryOfStringsVariable(data.COOKIES) { Name = name }; } else if (name == "HEADERS") { v = new DictionaryOfStringsVariable(data.HEADERS) { Name = name }; } else { v = globals.Get(name); } } if (v == null) { continue; } // Parse the arguments var args = m.Replace(name, ""); switch (v) { case StringVariable: output = output.Replace(full, v.AsString()); break; case ListOfStringsVariable: // If it's just the list name, replace it with its string representation if (string.IsNullOrEmpty(args)) { output = output.Replace(full, v.AsString()); break; } var index = 0; int.TryParse(ParseArguments(args, '[', ']')[0], out index); var item = GetListItem(v.AsListOfStrings(), index); // Can return null if (item != null) { output = output.Replace(full, item); } break; case DictionaryOfStringsVariable: var dict = v.AsDictionaryOfStrings(); if (args.Contains("(") && args.Contains(")")) { var key = ParseArguments(args, '(', ')')[0]; if (dict.ContainsKey(key)) { output = output.Replace(full, dict[key]); } } else if (args.Contains("{") && args.Contains("}")) { var value = ParseArguments(args, '{', '}')[0]; if (dict.ContainsValue(value)) { output = output.Replace(full, dict.First(kvp => kvp.Value == value).Key); } } else // If it's just the dictionary name, replace it with its string representation { output = output.Replace(full, v.AsString()); break; } break; } } }while (original.Contains("<") && original.Contains(">") && output != previous); return(output); }