mirror of
https://github.com/openbullet/openbullet.git
synced 2023-10-21 07:33:42 -05:00
First commit - OpenBullet 1.0.0
This commit is contained in:
@@ -0,0 +1,370 @@
|
||||
using Newtonsoft.Json;
|
||||
using RuriLib.Models;
|
||||
using RuriLib.ViewModels;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace RuriLib
|
||||
{
|
||||
/// <summary>
|
||||
/// A generic block used to process information in RuriLib.
|
||||
/// </summary>
|
||||
public abstract class BlockBase : ViewModelBase
|
||||
{
|
||||
private string label = "BASE";
|
||||
/// <summary>The label of the block.</summary>
|
||||
public string Label { get { return label; } set { label = value; OnPropertyChanged(); } }
|
||||
|
||||
private bool disabled = false;
|
||||
/// <summary>Whether the block should be skipped or processed.</summary>
|
||||
public bool Disabled { get { return disabled; } set { disabled = value; OnPropertyChanged(); } }
|
||||
|
||||
/// <summary>Whether the block is a selenium-related block.</summary>
|
||||
[JsonIgnore]
|
||||
public bool IsSelenium { get { return GetType().ToString().StartsWith("S"); } }
|
||||
|
||||
/// <summary>Whether the block is a captcha-related block.</summary>
|
||||
[JsonIgnore]
|
||||
public bool IsCaptcha { get { return GetType() == typeof(BlockImageCaptcha) || GetType() == typeof(BlockRecaptcha); } }
|
||||
|
||||
#region Virtual methods
|
||||
/// <summary>
|
||||
/// Builds a block from a line of LoliScript code.
|
||||
/// </summary>
|
||||
/// <param name="line">The line of LoliScript code</param>
|
||||
/// <returns>The parsed block object</returns>
|
||||
public virtual BlockBase FromLS(string line)
|
||||
{
|
||||
throw new Exception("Cannot Convert to the abstract class BlockBase");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds a block from multiple lines of LoliScript code.
|
||||
/// </summary>
|
||||
/// <param name="lines">The lines of LoliScript code</param>
|
||||
/// <returns>The parsed block object</returns>
|
||||
public virtual BlockBase FromLS(List<string> lines)
|
||||
{
|
||||
throw new Exception("Cannot Convert from the abstract class BlockBase");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the block to LoliScript code.
|
||||
/// </summary>
|
||||
/// <param name="indent"></param>
|
||||
/// <returns></returns>
|
||||
public virtual string ToLS(bool indent = true)
|
||||
{
|
||||
throw new Exception("Cannot Convert from the abstract class BlockBase");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the actual block logic.
|
||||
/// </summary>
|
||||
/// <param name="data">The BotData needed for variable replacement</param>
|
||||
public virtual void Process(BotData data)
|
||||
{
|
||||
// Clear log buffer
|
||||
data.LogBuffer.Clear();
|
||||
data.Log(new LogEntry(string.Format("<--- Executing Block {0} --->", Label),Colors.Orange));
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Variable Replacement
|
||||
/// <summary>
|
||||
/// Replaces variables recursively, expanding lists or dictionaries with jolly indices.
|
||||
/// </summary>
|
||||
/// <param name="input">The string to replace variables into</param>
|
||||
/// <param name="data">The BotData needed for variable replacement</param>
|
||||
/// <returns>An array of values obtained replacing the original input with each of the possible values of the first List or Dictionary variable found</returns>
|
||||
public static List<string> ReplaceValuesRecursive(string input, BotData data)
|
||||
{
|
||||
var toReplace = new List<string>();
|
||||
|
||||
// Regex parse the syntax <LIST[*]> (only the first one! This doesn't support multiple arrays because they can have different sizes)
|
||||
var match = Regex.Match(input, @"<([^\[]*)\[\*\]>");
|
||||
|
||||
if (match.Success)
|
||||
{
|
||||
var full = match.Groups[0].Value;
|
||||
var name = match.Groups[1].Value;
|
||||
|
||||
// Retrieve the dict
|
||||
var list = data.Variables.GetList(name);
|
||||
if (list == null) list = data.GlobalVariables.GetList(name);
|
||||
|
||||
// If there's no corresponding variable, just readd the input string and proceed with normal replacement
|
||||
if (list == null) toReplace.Add(input);
|
||||
else
|
||||
{
|
||||
foreach (var item in list)
|
||||
toReplace.Add(input.Replace(full, item));
|
||||
}
|
||||
goto END;
|
||||
}
|
||||
|
||||
// Regex parse the syntax <DICT(*)> (wildcard key -> returns list of all values)
|
||||
match = Regex.Match(input, @"<([^\(]*)\(\*\)>");
|
||||
|
||||
if (match.Success)
|
||||
{
|
||||
var full = match.Groups[0].Value;
|
||||
var name = match.Groups[1].Value;
|
||||
|
||||
// Retrieve the list
|
||||
var dict = data.Variables.GetDictionary(name);
|
||||
if (dict == null) dict = data.GlobalVariables.GetDictionary(name);
|
||||
|
||||
// If there's no corresponding variable, just readd the input string and proceed with normal replacement
|
||||
if (dict == null) toReplace.Add(input);
|
||||
else
|
||||
{
|
||||
foreach (var item in dict)
|
||||
toReplace.Add(input.Replace(full, item.Value));
|
||||
}
|
||||
goto END;
|
||||
}
|
||||
|
||||
// Regex parse the syntax <DICT{*}> (wildcard value -> returns list of all keys)
|
||||
match = Regex.Match(input, @"<([^\{]*)\{\*\}>");
|
||||
|
||||
if (match.Success)
|
||||
{
|
||||
var full = match.Groups[0].Value;
|
||||
var name = match.Groups[1].Value;
|
||||
|
||||
// Retrieve the dict
|
||||
var dict = data.Variables.GetDictionary(name);
|
||||
if (dict == null) dict = data.GlobalVariables.GetDictionary(name);
|
||||
|
||||
// If there's no corresponding variable, just readd the input string and proceed with normal replacement
|
||||
if (dict == null) toReplace.Add(input);
|
||||
else
|
||||
{
|
||||
foreach (var item in dict)
|
||||
toReplace.Add(input.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(input);
|
||||
|
||||
END:
|
||||
// Now for each item in the list, do the normal replacement and return the replaced list of strings
|
||||
return toReplace.Select(i => ReplaceValues(i, data)).ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replaces variables in a given input string.
|
||||
/// </summary>
|
||||
/// <param name="input">The string to replace variables into</param>
|
||||
/// <param name="data">The BotData needed for variable replacement</param>
|
||||
/// <returns>The string where variables have been replaced</returns>
|
||||
public static string ReplaceValues(string input, BotData data)
|
||||
{
|
||||
if (!input.Contains("<") && !input.Contains(">")) return input;
|
||||
|
||||
var previous = "";
|
||||
var output = input;
|
||||
|
||||
do
|
||||
{
|
||||
previous = output;
|
||||
|
||||
// Replace all the fixed quantities (this needs to go away inside BotData.cs, initialized as hidden vars)
|
||||
output = output.Replace("<INPUT>", data.Data.Data);
|
||||
output = output.Replace("<STATUS>", data.Status.ToString());
|
||||
output = output.Replace("<BOTNUM>", data.BotNumber.ToString());
|
||||
if (data.Proxy != null)
|
||||
output = output.Replace("<PROXY>", data.Proxy.Proxy);
|
||||
|
||||
// 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 = data.Variables.Get(name);
|
||||
if (v == null) v = data.GlobalVariables.Get(name);
|
||||
if (v == null) continue;
|
||||
|
||||
// Parse the arguments
|
||||
var args = m.Replace(name, "");
|
||||
|
||||
switch (v.Type)
|
||||
{
|
||||
case CVar.VarType.Single:
|
||||
output = output.Replace(full, v.Value);
|
||||
break;
|
||||
|
||||
case CVar.VarType.List:
|
||||
var index = 0;
|
||||
int.TryParse(ParseArguments(args, '[', ']')[0], out index);
|
||||
var item = v.GetListItem(index); // Can return null
|
||||
if (item != null) output = output.Replace(full, item);
|
||||
break;
|
||||
|
||||
case CVar.VarType.Dictionary:
|
||||
if (args.Contains("(") && args.Contains(")"))
|
||||
{
|
||||
var dicKey = ParseArguments(args, '(', ')')[0];
|
||||
output = output.Replace(full, v.GetDictValue(dicKey));
|
||||
}
|
||||
else if (args.Contains("{") && args.Contains("}"))
|
||||
{
|
||||
var dicVal = ParseArguments(args, '{', '}')[0];
|
||||
output = output.Replace(full, v.GetDictKey(dicVal));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
while (input.Contains("<") && input.Contains(">") && output != previous);
|
||||
|
||||
return output;
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Parses an argument between two bracket delimiters.
|
||||
/// </summary>
|
||||
/// <param name="input">The string to parse the argument from</param>
|
||||
/// <param name="delimL">The left bracket delimiter</param>
|
||||
/// <param name="delimR">The right bracket delimiter</param>
|
||||
/// <returns>The argument between the delimiters</returns>
|
||||
public static List<string> ParseArguments(string input, char delimL, char delimR)
|
||||
{
|
||||
var output = new List<string>();
|
||||
var matches = Regex.Matches(input, @"\" + delimL + @"([^\" + delimR + @"]*)\" + delimR);
|
||||
foreach (Match match in matches) output.Add(match.Groups[1].Value);
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the ADDRESS and SOURCE variables basing on the selenium-driven browser's URL bar and page source.
|
||||
/// </summary>
|
||||
/// <param name="data">The BotData containing the driver and the variables</param>
|
||||
public static void UpdateSeleniumData(BotData data)
|
||||
{
|
||||
data.Address = data.Driver.Url;
|
||||
data.ResponseSource = data.Driver.PageSource;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a single or list variable with the given value.
|
||||
/// </summary>
|
||||
/// <param name="data">The BotData used for variable replacement and insertion</param>
|
||||
/// <param name="isCapture">Whether the variable should be marked for Capture</param>
|
||||
/// <param name="recursive">Whether the variable to add should be a list or a single value</param>
|
||||
/// <param name="values">The list of values. In case recursive is set to false, only the first value of the list will be taken.</param>
|
||||
/// <param name="variableName">The name of the variable to create</param>
|
||||
/// <param name="prefix">The string to add at the start of the value</param>
|
||||
/// <param name="suffix">The string to add at the end of the value</param>
|
||||
public static void InsertVariables(BotData data, bool isCapture, bool recursive, List<string> values, string variableName, string prefix, string suffix)
|
||||
{
|
||||
var list = values.Select(v => ReplaceValues(prefix, data) + v.Trim() + ReplaceValues(suffix, data));
|
||||
CVar variable;
|
||||
if (recursive) variable = new CVar(variableName, list.ToList(), isCapture);
|
||||
else variable = new CVar(variableName, list.First(), isCapture);
|
||||
data.Variables.Set(variable);
|
||||
data.Log(new LogEntry("Parsed variable" + " | Name: " + variable.Name + " | Value: " + variable.ToString() + Environment.NewLine, isCapture ? Colors.OrangeRed : Colors.Gold));
|
||||
}
|
||||
|
||||
#region File Utilities
|
||||
/// <summary>
|
||||
/// Saves a Selenium screenshot to a file with automatically generated name.
|
||||
/// </summary>
|
||||
/// <param name="screenshot">The Selenium screenshot</param>
|
||||
/// <param name="data">The BotData used for path creation</param>
|
||||
public static void SaveScreenshot(OpenQA.Selenium.Screenshot screenshot, BotData data)
|
||||
{
|
||||
var path = MakeScreenshotPath(data);
|
||||
data.Screenshots.Add(path);
|
||||
screenshot.SaveAsFile(path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves a screenshot to a file with automatically generated name.
|
||||
/// </summary>
|
||||
/// <param name="screenshot">The bitmap image</param>
|
||||
/// <param name="data">The BotData used for path creation</param>
|
||||
public static void SaveScreenshot(Bitmap screenshot, BotData data)
|
||||
{
|
||||
var path = MakeScreenshotPath(data);
|
||||
data.Screenshots.Add(path);
|
||||
screenshot.Save(path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds the path for the screenshot file.
|
||||
/// </summary>
|
||||
/// <param name="data">The BotData for path creation</param>
|
||||
/// <returns>The path of the file to save the screenshot to</returns>
|
||||
private static string MakeScreenshotPath(BotData data)
|
||||
{
|
||||
var folderName = MakeValidFileName(data.ConfigSettings.Name);
|
||||
var originalFilename = MakeValidFileName(data.Data.Data);
|
||||
// Check if you have to make the folder
|
||||
if (!Directory.Exists($"Screenshots\\{folderName}")) Directory.CreateDirectory($"Screenshots\\{folderName}");
|
||||
|
||||
// Save the file inside the folder
|
||||
var filename = GetFirstAvailableFileName($"Screenshots\\{folderName}\\", originalFilename, "bmp");
|
||||
return $"Screenshots\\{folderName}\\{filename}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the first available name in the given folder by incrementing a number at the end of the filename.
|
||||
/// </summary>
|
||||
/// <param name="basePath">The path to the folder</param>
|
||||
/// <param name="fileName">The name of the file without numbers at the end</param>
|
||||
/// <param name="extension">The extension of the file</param>
|
||||
/// <returns>The first available filename (including extension)</returns>
|
||||
public static string GetFirstAvailableFileName(string basePath, string fileName, string extension)
|
||||
{
|
||||
int i;
|
||||
for (i = 1; File.Exists(basePath + fileName + i + "." + extension); i++) { }
|
||||
return fileName + i + "." + extension;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fixes the filename to be compatible with the filesystem indicization.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the file</param>
|
||||
/// <param name="underscore">Whether to replace the unallowed characters with an underscore instead of removing them</param>
|
||||
/// <returns>The valid filename ready to be saved to disk</returns>
|
||||
public static string MakeValidFileName(string name, bool underscore = true)
|
||||
{
|
||||
string invalidChars = Regex.Escape(new string(Path.GetInvalidFileNameChars()));
|
||||
string invalidRegStr = string.Format(@"([{0}]*\.+$)|([{0}]+)", invalidChars);
|
||||
|
||||
return Regex.Replace(name, invalidRegStr, underscore ? "_" : "");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Truncates a string to fit a given size and adds ' [...]' (5 characters) at the end to display that the string would be longer.
|
||||
/// </summary>
|
||||
/// <param name="input">The string to truncate</param>
|
||||
/// <param name="max">The maximum length of the string</param>
|
||||
/// <returns>The truncated string, or the same string if its length was not exceeding the maximum size</returns>
|
||||
public static string TruncatePretty(string input, int max)
|
||||
{
|
||||
input = input.Replace("\r\n", "").Replace("\n", "");
|
||||
if (input.Length < max) return input;
|
||||
return input.Substring(0, max) + " [...]";
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
using RuriLib.LS;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Windows.Media;
|
||||
using System.Threading;
|
||||
using Jint;
|
||||
using Leaf.xNet;
|
||||
using System.Net;
|
||||
using Leaf.xNet.Services.Cloudflare;
|
||||
using Leaf.xNet.Services.Captcha;
|
||||
|
||||
namespace RuriLib
|
||||
{
|
||||
/// <summary>
|
||||
/// A block that can bypass Cloudflare protections.
|
||||
/// </summary>
|
||||
public class BlockBypassCF : BlockBase
|
||||
{
|
||||
private string url = "";
|
||||
/// <summary>The URL of the Cloudflare-protected website.</summary>
|
||||
public string Url { get { return url; } set { url = value; OnPropertyChanged(); } }
|
||||
|
||||
private string userAgent = "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko";
|
||||
/// <summary>The User-Agent header to use when solving the challenge.</summary>
|
||||
public string UserAgent { get { return userAgent; } set { userAgent = value; OnPropertyChanged(); } }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a Cloudflare bypass block.
|
||||
/// </summary>
|
||||
public BlockBypassCF()
|
||||
{
|
||||
Label = "BYPASS CF";
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override BlockBase FromLS(string line)
|
||||
{
|
||||
// Trim the line
|
||||
var input = line.Trim();
|
||||
|
||||
// Parse the label
|
||||
if (input.StartsWith("#"))
|
||||
Label = LineParser.ParseLabel(ref input);
|
||||
|
||||
/*
|
||||
* Syntax
|
||||
* BYPASSCF "URL" ["UA"]
|
||||
* */
|
||||
|
||||
Url = LineParser.ParseLiteral(ref input, "URL");
|
||||
|
||||
if (input != "") UserAgent = LineParser.ParseLiteral(ref input, "UA");
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToLS(bool indent = true)
|
||||
{
|
||||
var writer = new BlockWriter(GetType(), indent, Disabled);
|
||||
writer
|
||||
.Label(Label)
|
||||
.Token("BYPASSCF")
|
||||
.Literal(Url)
|
||||
.Literal(UserAgent, "UserAgent");
|
||||
return writer.ToString();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Process(BotData data)
|
||||
{
|
||||
base.Process(data);
|
||||
|
||||
// If the clearance info is already set and we're not getting it fresh each time, skip
|
||||
if (data.UseProxies)
|
||||
{
|
||||
if(data.Proxy.Clearance != "" && data.Proxy.Cfduid != "" && !data.GlobalSettings.Proxies.AlwaysGetClearance)
|
||||
{
|
||||
data.Log(new LogEntry("Skipping CF Bypass because there is already a valid cookie", Colors.White));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var localUrl = ReplaceValues(url, data);
|
||||
|
||||
var timeout = data.GlobalSettings.General.RequestTimeout * 1000;
|
||||
|
||||
var request = new HttpRequest();
|
||||
request.IgnoreProtocolErrors = true;
|
||||
request.ConnectTimeout = timeout;
|
||||
request.ReadWriteTimeout = timeout;
|
||||
request.Cookies = new CookieStorage();
|
||||
foreach (var cookie in data.Cookies)
|
||||
request.Cookies.Add(new Cookie(cookie.Key, cookie.Value));
|
||||
|
||||
if (data.UseProxies)
|
||||
{
|
||||
switch (data.Proxy.Type)
|
||||
{
|
||||
case Extreme.Net.ProxyType.Http:
|
||||
request.Proxy = HttpProxyClient.Parse(data.Proxy.Proxy);
|
||||
break;
|
||||
|
||||
case Extreme.Net.ProxyType.Socks4:
|
||||
request.Proxy = Socks4ProxyClient.Parse(data.Proxy.Proxy);
|
||||
break;
|
||||
|
||||
case Extreme.Net.ProxyType.Socks4a:
|
||||
request.Proxy = Socks4AProxyClient.Parse(data.Proxy.Proxy);
|
||||
break;
|
||||
|
||||
case Extreme.Net.ProxyType.Socks5:
|
||||
request.Proxy = Socks5ProxyClient.Parse(data.Proxy.Proxy);
|
||||
break;
|
||||
|
||||
case Extreme.Net.ProxyType.Chain:
|
||||
throw new Exception("The Chain Proxy Type is not supported in Leaf.xNet (used for CF Bypass).");
|
||||
}
|
||||
|
||||
request.Proxy.ReadWriteTimeout = timeout;
|
||||
request.Proxy.ConnectTimeout = timeout;
|
||||
request.Proxy.Username = data.Proxy.Username;
|
||||
request.Proxy.Password = data.Proxy.Password;
|
||||
}
|
||||
|
||||
request.UserAgent = ReplaceValues(userAgent, data);
|
||||
|
||||
var twoCapToken = data.GlobalSettings.Captchas.TwoCapToken;
|
||||
if (twoCapToken != "") request.CaptchaSolver = new TwoCaptchaSolver() { ApiKey = data.GlobalSettings.Captchas.TwoCapToken };
|
||||
|
||||
var response = request.GetThroughCloudflare(new Uri(localUrl));
|
||||
var cookies = response.Cookies.GetCookies(localUrl);
|
||||
var clearanceCookie = cookies["cf_clearance"];
|
||||
var cfduidCookie = cookies["__cfduid"];
|
||||
data.Cookies.Add("cf_clearance", clearanceCookie.Value);
|
||||
data.Proxy.Clearance = clearanceCookie.Value;
|
||||
data.Cookies.Add("__cfduid", cfduidCookie.Value);
|
||||
data.Proxy.Cfduid = cfduidCookie.Value;
|
||||
data.Log(new LogEntry("Got Cloudflare clearance!", Colors.GreenYellow));
|
||||
data.Log(new LogEntry(clearanceCookie + Environment.NewLine + cfduidCookie + Environment.NewLine, Colors.White));
|
||||
|
||||
// Get code
|
||||
data.ResponseCode = ((int)response.StatusCode).ToString();
|
||||
data.Log(new LogEntry("Response code: " + data.ResponseCode, Colors.Cyan));
|
||||
|
||||
// Get headers
|
||||
data.Log(new LogEntry("Received headers:", Colors.DeepPink));
|
||||
var headerList = new List<KeyValuePair<string, string>>();
|
||||
var receivedHeaders = response.EnumerateHeaders();
|
||||
data.ResponseHeaders.Clear();
|
||||
while (receivedHeaders.MoveNext())
|
||||
{
|
||||
var header = receivedHeaders.Current;
|
||||
data.ResponseHeaders.Add(header.Key, header.Value);
|
||||
data.Log(new LogEntry(header.Key + ": " + header.Value, Colors.LightPink));
|
||||
}
|
||||
|
||||
// Get cookies
|
||||
data.Log(new LogEntry("Received cookies:", Colors.Goldenrod));
|
||||
foreach (Cookie cookie in response.Cookies.GetCookies(localUrl))
|
||||
{
|
||||
if (data.Cookies.ContainsKey(cookie.Name)) data.Cookies[cookie.Name] = cookie.Value;
|
||||
else data.Cookies.Add(cookie.Name, cookie.Value);
|
||||
data.Log(new LogEntry(cookie.Name + ": " + cookie.Value, Colors.LightGoldenrodYellow));
|
||||
}
|
||||
|
||||
data.ResponseSource = response.ToString();
|
||||
data.Log(new LogEntry("Response Source:", Colors.Green));
|
||||
data.Log(new LogEntry(data.ResponseSource, Colors.GreenYellow));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
using Newtonsoft.Json;
|
||||
using RuriLib.CaptchaServices;
|
||||
using RuriLib.LS;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace RuriLib
|
||||
{
|
||||
/// <summary>
|
||||
/// A block that can solve captcha challenges.
|
||||
/// </summary>
|
||||
public abstract class BlockCaptcha : BlockBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The available Captcha-solving services.
|
||||
/// </summary>
|
||||
public enum CaptchaService
|
||||
{
|
||||
/// <summary>The service provided by https://anti-captcha.com/</summary>
|
||||
AntiCaptcha,
|
||||
|
||||
/// <summary>The service provided by https://azcaptcha.com/</summary>
|
||||
AZCaptcha,
|
||||
|
||||
/// <summary>The service provided by https://captchas.io/</summary>
|
||||
CaptchasIO,
|
||||
|
||||
/// <summary>The service provided by https://www.deathbycaptcha.com/</summary>
|
||||
DBC,
|
||||
|
||||
/// <summary>The service provided by http://de-captcher.com/</summary>
|
||||
DeCaptcher,
|
||||
|
||||
/// <summary>The service provided by http://www.imagetyperz.com/</summary>
|
||||
ImageTypers,
|
||||
|
||||
/// <summary>The service provided by https://www.solverecaptcha.com/</summary>
|
||||
SolveRecaptcha,
|
||||
|
||||
/// <summary>The service provided by https://2captcha.com/</summary>
|
||||
TwoCaptcha
|
||||
}
|
||||
|
||||
/// <summary>The balance of the account of the captcha-solving service.</summary>
|
||||
[JsonIgnore]
|
||||
public double Balance { get; set; } = 0;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Process(BotData data)
|
||||
{
|
||||
base.Process(data);
|
||||
|
||||
// If bypass balance check, skip this method.
|
||||
if (data.GlobalSettings.Captchas.BypassBalanceCheck) return;
|
||||
|
||||
// Get balance. If balance is under a certain threshold, don't ask for captcha solve
|
||||
Balance = 0; // Reset it or the block will save it for future calls
|
||||
data.Log(new LogEntry("Checking balance...", Colors.White));
|
||||
switch (data.GlobalSettings.Captchas.CurrentService)
|
||||
{
|
||||
case CaptchaService.ImageTypers:
|
||||
Balance = new ImageTyperz(data.GlobalSettings.Captchas.ImageTypToken, 0).GetBalance();
|
||||
break;
|
||||
|
||||
case CaptchaService.AntiCaptcha:
|
||||
Balance = new AntiCaptcha(data.GlobalSettings.Captchas.AntiCapToken, 0).GetBalance();
|
||||
break;
|
||||
|
||||
case CaptchaService.DBC:
|
||||
Balance = new DeathByCaptcha(data.GlobalSettings.Captchas.DBCUser, data.GlobalSettings.Captchas.DBCPass, 0).GetBalance();
|
||||
break;
|
||||
|
||||
case CaptchaService.TwoCaptcha:
|
||||
Balance = new TwoCaptcha(data.GlobalSettings.Captchas.TwoCapToken, 0).GetBalance();
|
||||
break;
|
||||
|
||||
case CaptchaService.DeCaptcher:
|
||||
Balance = new DeCaptcher(data.GlobalSettings.Captchas.DCUser, data.GlobalSettings.Captchas.DCPass, 0).GetBalance();
|
||||
break;
|
||||
|
||||
case CaptchaService.AZCaptcha:
|
||||
Balance = new AZCaptcha(data.GlobalSettings.Captchas.AZCapToken, 0).GetBalance();
|
||||
break;
|
||||
|
||||
case CaptchaService.SolveRecaptcha:
|
||||
Balance = new SolveReCaptcha(data.GlobalSettings.Captchas.SRUserId, data.GlobalSettings.Captchas.SRToken, 0).GetBalance();
|
||||
break;
|
||||
|
||||
case CaptchaService.CaptchasIO:
|
||||
Balance = new CaptchasIO(data.GlobalSettings.Captchas.CIOToken, 0).GetBalance();
|
||||
break;
|
||||
|
||||
default:
|
||||
Balance = 999;
|
||||
break;
|
||||
}
|
||||
|
||||
if (Balance <= 0) throw new System.Exception($"[{data.GlobalSettings.Captchas.CurrentService}] Bad token/credentials or zero balance!");
|
||||
|
||||
data.Log(new LogEntry($"[{data.GlobalSettings.Captchas.CurrentService}] Current Balance: ${Balance}", Colors.GreenYellow));
|
||||
data.Balance = Balance;
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,215 @@
|
||||
using Extreme.Net;
|
||||
using RuriLib.CaptchaServices;
|
||||
using RuriLib.LS;
|
||||
using RuriLib.Models;
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace RuriLib
|
||||
{
|
||||
/// <summary>
|
||||
/// A block that solves an image captcha challenge.
|
||||
/// </summary>
|
||||
public class BlockImageCaptcha : BlockCaptcha
|
||||
{
|
||||
private string url = "";
|
||||
/// <summary>The URL to download the captcha image from.</summary>
|
||||
public string Url { get { return url; } set { url = value; OnPropertyChanged(); } }
|
||||
|
||||
private string variableName = "";
|
||||
/// <summary>The name of the variable where the challenge solution will be stored.</summary>
|
||||
public string VariableName { get { return variableName; } set { variableName = value; OnPropertyChanged(); } }
|
||||
|
||||
private bool base64 = false;
|
||||
/// <summary>Whether the Url is a base64-encoded captcha image.</summary>
|
||||
public bool Base64 { get { return base64; } set { base64 = value; OnPropertyChanged(); } }
|
||||
|
||||
private bool sendScreenshot = false;
|
||||
/// <summary>Whether the captcha image needs to be taken by the last screenshot taken by selenium.</summary>
|
||||
public bool SendScreenshot { get { return sendScreenshot; } set { sendScreenshot = value; OnPropertyChanged(); } }
|
||||
|
||||
/// <summary>
|
||||
/// Creates an Image Captcha block.
|
||||
/// </summary>
|
||||
public BlockImageCaptcha()
|
||||
{
|
||||
Label = "CAPTCHA";
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override BlockBase FromLS(string line)
|
||||
{
|
||||
// Trim the line
|
||||
var input = line.Trim();
|
||||
|
||||
// Parse the label
|
||||
if (input.StartsWith("#"))
|
||||
Label = LineParser.ParseLabel(ref input);
|
||||
|
||||
/*
|
||||
* Syntax:
|
||||
* CAPTCHA "URL" [BASE64? USESCREEN?] -> VAR "CAP"
|
||||
* */
|
||||
|
||||
Url = LineParser.ParseLiteral(ref input, "URL");
|
||||
|
||||
while (LineParser.Lookahead(ref input) == TokenType.Boolean)
|
||||
LineParser.SetBool(ref input, this);
|
||||
|
||||
LineParser.EnsureIdentifier(ref input, "->");
|
||||
LineParser.EnsureIdentifier(ref input, "VAR");
|
||||
|
||||
// Parse the variable name
|
||||
VariableName = LineParser.ParseLiteral(ref input, "VARIABLE NAME");
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToLS(bool indent = true)
|
||||
{
|
||||
var writer = new BlockWriter(GetType(), indent, Disabled);
|
||||
writer
|
||||
.Label(Label)
|
||||
.Token("CAPTCHA")
|
||||
.Literal(Url)
|
||||
.Boolean(Base64, "Base64")
|
||||
.Boolean(SendScreenshot, "SendScreenshot")
|
||||
.Arrow()
|
||||
.Token("VAR")
|
||||
.Literal(VariableName);
|
||||
return writer.ToString();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Process(BotData data)
|
||||
{
|
||||
if(!data.GlobalSettings.Captchas.BypassBalanceCheck)
|
||||
base.Process(data);
|
||||
|
||||
var localUrl = ReplaceValues(url, data);
|
||||
|
||||
data.Log(new LogEntry("Downloading image...", Colors.White));
|
||||
|
||||
// Download captcha
|
||||
var captchaFile = string.Format("Captchas/captcha{0}.jpg", data.BotNumber);
|
||||
if (base64)
|
||||
{
|
||||
var bytes = Convert.FromBase64String(localUrl);
|
||||
using (var imageFile = new FileStream(captchaFile, FileMode.Create))
|
||||
{
|
||||
imageFile.Write(bytes, 0, bytes.Length);
|
||||
imageFile.Flush();
|
||||
}
|
||||
}
|
||||
else if (sendScreenshot && data.Screenshots.Count > 0)
|
||||
{
|
||||
Bitmap image = new Bitmap(data.Screenshots.Last());
|
||||
image.Save(captchaFile);
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
DownloadRemoteImageFile(captchaFile, data, localUrl);
|
||||
}
|
||||
catch (Exception ex) { data.Log(new LogEntry(ex.Message, Colors.Tomato)); throw; }
|
||||
}
|
||||
|
||||
string response = "";
|
||||
CaptchaServices.CaptchaService service = null;
|
||||
var bitmap = new Bitmap(captchaFile);
|
||||
try
|
||||
{
|
||||
switch (data.GlobalSettings.Captchas.CurrentService)
|
||||
{
|
||||
case CaptchaService.ImageTypers:
|
||||
service = new ImageTyperz(data.GlobalSettings.Captchas.ImageTypToken, data.GlobalSettings.Captchas.Timeout);
|
||||
break;
|
||||
|
||||
case CaptchaService.AntiCaptcha:
|
||||
service = new AntiCaptcha(data.GlobalSettings.Captchas.AntiCapToken, data.GlobalSettings.Captchas.Timeout);
|
||||
break;
|
||||
|
||||
case CaptchaService.DBC:
|
||||
service = new DeathByCaptcha(data.GlobalSettings.Captchas.DBCUser, data.GlobalSettings.Captchas.DBCPass, data.GlobalSettings.Captchas.Timeout);
|
||||
break;
|
||||
|
||||
case CaptchaService.TwoCaptcha:
|
||||
service = new TwoCaptcha(data.GlobalSettings.Captchas.TwoCapToken, data.GlobalSettings.Captchas.Timeout);
|
||||
break;
|
||||
|
||||
case CaptchaService.DeCaptcher:
|
||||
service = new DeCaptcher(data.GlobalSettings.Captchas.DCUser, data.GlobalSettings.Captchas.DCPass, data.GlobalSettings.Captchas.Timeout);
|
||||
break;
|
||||
|
||||
case CaptchaService.AZCaptcha:
|
||||
service = new AZCaptcha(data.GlobalSettings.Captchas.AZCapToken, data.GlobalSettings.Captchas.Timeout);
|
||||
break;
|
||||
|
||||
case CaptchaService.CaptchasIO:
|
||||
service = new CaptchasIO(data.GlobalSettings.Captchas.CIOToken, data.GlobalSettings.Captchas.Timeout);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Exception("This service cannot solve normal captchas!");
|
||||
}
|
||||
response = service.SolveCaptcha(bitmap);
|
||||
}
|
||||
catch(Exception ex) { data.Log(new LogEntry(ex.Message, Colors.Tomato)); throw; }
|
||||
finally { bitmap.Dispose(); }
|
||||
|
||||
data.CaptchaService = service;
|
||||
data.Log(response == "" ? new LogEntry("Couldn't get a response from the service", Colors.Tomato) : new LogEntry("Succesfully got the response: " + response, Colors.GreenYellow));
|
||||
|
||||
if (VariableName != "")
|
||||
{
|
||||
data.Log(new LogEntry("Response stored in variable: " + variableName, Colors.White));
|
||||
data.Variables.Set(new CVar(variableName, response));
|
||||
}
|
||||
}
|
||||
|
||||
private void DownloadRemoteImageFile(string fileName, BotData data, string localUrl)
|
||||
{
|
||||
HttpRequest request = new HttpRequest();
|
||||
|
||||
request.Cookies = new CookieDictionary();
|
||||
foreach (var cookie in data.Cookies)
|
||||
request.Cookies.Add(cookie.Key, cookie.Value);
|
||||
|
||||
// Set proxy
|
||||
if (data.UseProxies)
|
||||
{
|
||||
request.Proxy = data.Proxy.GetClient();
|
||||
|
||||
var timeout = data.GlobalSettings.General.RequestTimeout * 1000;
|
||||
try
|
||||
{
|
||||
request.Proxy.ReadWriteTimeout = timeout;
|
||||
request.Proxy.ConnectTimeout = timeout;
|
||||
request.Proxy.Username = data.Proxy.Username;
|
||||
request.Proxy.Password = data.Proxy.Password;
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
HttpResponse response = request.Get(localUrl);
|
||||
|
||||
using (Stream inputStream = response.ToMemoryStream())
|
||||
using (Stream outputStream = File.OpenWrite(fileName))
|
||||
{
|
||||
byte[] buffer = new byte[4096];
|
||||
int bytesRead;
|
||||
do
|
||||
{
|
||||
bytesRead = inputStream.Read(buffer, 0, buffer.Length);
|
||||
outputStream.Write(buffer, 0, bytesRead);
|
||||
} while (bytesRead != 0);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
using RuriLib.LS;
|
||||
using RuriLib.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace RuriLib
|
||||
{
|
||||
/// <summary>
|
||||
/// A block that changes the bot status according to some conditions.
|
||||
/// </summary>
|
||||
public class BlockKeycheck : BlockBase
|
||||
{
|
||||
private bool banOn4XX = false;
|
||||
/// <summary>Whether to set the bot status to BAN if the last HTTP code was of type 4.</summary>
|
||||
public bool BanOn4XX { get { return banOn4XX; } set { banOn4XX = value; OnPropertyChanged(); } }
|
||||
|
||||
private bool banOnToCheck = true;
|
||||
/// <summary>Whether to set the bot status as BAN if no keychain was valid.</summary>
|
||||
public bool BanOnToCheck { get { return banOnToCheck; } set { banOnToCheck = value; OnPropertyChanged(); } }
|
||||
|
||||
/// <summary>The list of all keychains.</summary>
|
||||
public List<KeyChain> KeyChains = new List<KeyChain>();
|
||||
|
||||
/// <summary>
|
||||
/// Creates a KeyCheck block.
|
||||
/// </summary>
|
||||
public BlockKeycheck()
|
||||
{
|
||||
Label = "KEY CHECK";
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override BlockBase FromLS(string line)
|
||||
{
|
||||
// Trim the line
|
||||
var input = line.Trim();
|
||||
|
||||
// Parse the label
|
||||
if (input.StartsWith("#"))
|
||||
Label = LineParser.ParseLabel(ref input);
|
||||
|
||||
/*
|
||||
* Syntax:
|
||||
* KEYCHECK [BAN4XX? BANTOCHECK?] [KEYCHAIN TYPE [CUSTOMNAME] MODE [KEY "STRING" [CONDITION "STRING"]]*]*
|
||||
* */
|
||||
|
||||
while (LineParser.Lookahead(ref input) == TokenType.Boolean)
|
||||
LineParser.SetBool(ref input, this);
|
||||
|
||||
while (input != "") // Scan for keychains until the end
|
||||
{
|
||||
LineParser.EnsureIdentifier(ref input, "KEYCHAIN");
|
||||
|
||||
KeyChain kc = new KeyChain();
|
||||
|
||||
kc.Type = (KeyChain.KeychainType)LineParser.ParseEnum(ref input, "Keychain Type", typeof(KeyChain.KeychainType));
|
||||
if (kc.Type == KeyChain.KeychainType.Custom && LineParser.Lookahead(ref input) == TokenType.Literal)
|
||||
kc.CustomType = LineParser.ParseLiteral(ref input, "Custom Type");
|
||||
kc.Mode = (KeyChain.KeychainMode)LineParser.ParseEnum(ref input, "Keychain Mode", typeof(KeyChain.KeychainMode));
|
||||
|
||||
while (!input.StartsWith("KEYCHAIN") && input != "") // Scan for keys
|
||||
{
|
||||
Key k = new Key();
|
||||
LineParser.EnsureIdentifier(ref input, "KEY");
|
||||
|
||||
var first = LineParser.ParseLiteral(ref input, "Left Term");
|
||||
// If there is another key/keychain/nothing, the user used the short syntax
|
||||
if (LineParser.CheckIdentifier(ref input, "KEY") || LineParser.CheckIdentifier(ref input, "KEYCHAIN") || input == "")
|
||||
{
|
||||
k.LeftTerm = "<SOURCE>";
|
||||
k.Condition = Condition.Contains;
|
||||
k.RightTerm = first;
|
||||
}
|
||||
// Otherwise use the long syntax
|
||||
else
|
||||
{
|
||||
k.LeftTerm = first;
|
||||
k.Condition = (Condition)LineParser.ParseEnum(ref input, "Condition", typeof(Condition));
|
||||
if (k.Condition != Condition.Exists)
|
||||
k.RightTerm = LineParser.ParseLiteral(ref input, "Right Term");
|
||||
}
|
||||
|
||||
kc.Keys.Add(k);
|
||||
}
|
||||
|
||||
KeyChains.Add(kc);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToLS(bool indent = true)
|
||||
{
|
||||
var writer = new BlockWriter(GetType(), indent, Disabled);
|
||||
writer
|
||||
.Label(Label)
|
||||
.Token("KEYCHECK")
|
||||
.Boolean(BanOn4XX, "BanOn4XX")
|
||||
.Boolean(BanOnToCheck, "BanOnToCheck");
|
||||
|
||||
foreach(var kc in KeyChains)
|
||||
{
|
||||
writer
|
||||
.Indent(1)
|
||||
.Token("KEYCHAIN")
|
||||
.Token(kc.Type);
|
||||
|
||||
if (kc.Type == KeyChain.KeychainType.Custom)
|
||||
writer.Literal(kc.CustomType);
|
||||
|
||||
writer.Token(kc.Mode);
|
||||
|
||||
foreach(var k in kc.Keys)
|
||||
{
|
||||
if (k.LeftTerm == "<SOURCE>" && k.Condition == Condition.Contains)
|
||||
{
|
||||
writer
|
||||
.Indent(2)
|
||||
.Token("KEY")
|
||||
.Literal(k.RightTerm);
|
||||
}
|
||||
else
|
||||
{
|
||||
writer
|
||||
.Indent(2)
|
||||
.Token("KEY")
|
||||
.Literal(k.LeftTerm)
|
||||
.Token(k.Condition);
|
||||
|
||||
if (k.Condition != Condition.Exists)
|
||||
writer.Literal(k.RightTerm);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return writer.ToString();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Process(BotData data)
|
||||
{
|
||||
base.Process(data);
|
||||
|
||||
if (data.ResponseCode.StartsWith("4") && banOn4XX)
|
||||
{
|
||||
data.Status = BotStatus.BAN;
|
||||
return;
|
||||
}
|
||||
|
||||
// Check Global Keys
|
||||
foreach (var key in data.GlobalSettings.Proxies.GlobalBanKeys)
|
||||
{
|
||||
if (key != "" && data.ResponseSource.Contains(key))
|
||||
{
|
||||
data.Status = BotStatus.BAN;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// KEYS WILL BE CHECKED IN THE ORDER IN WHICH THEY APPEAR IN THE LIST, THE LAST KEY HAS ABSOLUTE PRIORITY
|
||||
bool found = false;
|
||||
foreach (var keychain in KeyChains)
|
||||
{
|
||||
if (keychain.CheckKeys(data))
|
||||
{
|
||||
found = true;
|
||||
switch (keychain.Type)
|
||||
{
|
||||
case KeyChain.KeychainType.Success:
|
||||
data.Status = BotStatus.SUCCESS;
|
||||
break;
|
||||
|
||||
case KeyChain.KeychainType.Failure:
|
||||
data.Status = BotStatus.FAIL;
|
||||
break;
|
||||
|
||||
case KeyChain.KeychainType.Ban:
|
||||
data.Status = BotStatus.BAN;
|
||||
break;
|
||||
|
||||
case KeyChain.KeychainType.Retry:
|
||||
data.Status = BotStatus.RETRY;
|
||||
break;
|
||||
|
||||
case KeyChain.KeychainType.Custom:
|
||||
data.Status = BotStatus.CUSTOM;
|
||||
data.CustomStatus = keychain.CustomType;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If at the end no key was found, set it to ban
|
||||
if (!found && banOnToCheck)
|
||||
data.Status = BotStatus.BAN;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace RuriLib
|
||||
{
|
||||
/// <summary>
|
||||
/// A block that contains LoliScript code for readonly visualization purposes.
|
||||
/// </summary>
|
||||
public class BlockLSCode : BlockBase
|
||||
{
|
||||
private string script = "";
|
||||
/// <summary>The LoliScript code to display.</summary>
|
||||
public string Script { get { return script; } set { script = value; OnPropertyChanged(); } }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a LSCode block.
|
||||
/// </summary>
|
||||
public BlockLSCode()
|
||||
{
|
||||
Label = "LS";
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override BlockBase FromLS(List<string> lines)
|
||||
{
|
||||
Script = string.Join(Environment.NewLine, lines);
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToLS(bool indent = true)
|
||||
{
|
||||
return script;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,466 @@
|
||||
using AngleSharp.Parser.Html;
|
||||
using Extreme.Net;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using RuriLib.LS;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace RuriLib
|
||||
{
|
||||
/// <summary>
|
||||
/// The allowed parsing algorithms.
|
||||
/// </summary>
|
||||
public enum ParseType
|
||||
{
|
||||
/// <summary>Algorithm that parses text between two strings.</summary>
|
||||
LR,
|
||||
|
||||
/// <summary>Algorithm that parses a given attribute from an HTML element identified by a CSS Selector.</summary>
|
||||
CSS,
|
||||
|
||||
/// <summary>Algorithm that parses values inside a json object.</summary>
|
||||
JSON,
|
||||
|
||||
/// <summary>Algorithm that parses a given attribute from an HTML element identified by xpath.</summary>
|
||||
XPATH,
|
||||
|
||||
/// <summary>Algorithm that extracts the text inside matched regex groups.</summary>
|
||||
REGEX
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A block that parses data from a string.
|
||||
/// </summary>
|
||||
public class BlockParse : BlockBase
|
||||
{
|
||||
private string parseTarget = "<SOURCE>";
|
||||
/// <summary>The string to parse data from.</summary>
|
||||
public string ParseTarget { get { return parseTarget; } set { parseTarget = value; OnPropertyChanged(); } }
|
||||
|
||||
private string variableName = "";
|
||||
/// <summary>The name of the output variable where the parsed text will be stored.</summary>
|
||||
public string VariableName { get { return variableName; } set { variableName = value; OnPropertyChanged(); } }
|
||||
|
||||
private bool isCapture = false;
|
||||
/// <summary>Whether the output variable should be marked as Capture.</summary>
|
||||
public bool IsCapture { get { return isCapture; } set { isCapture = value; OnPropertyChanged(); } }
|
||||
|
||||
private string prefix = "";
|
||||
/// <summary>The string to add to the beginning of the parsed data.</summary>
|
||||
public string Prefix { get { return prefix; } set { prefix = value; OnPropertyChanged(); } }
|
||||
|
||||
private string suffix = "";
|
||||
/// <summary>The string to add to the end of the parsed data.</summary>
|
||||
public string Suffix { get { return suffix; } set { suffix = value; OnPropertyChanged(); } }
|
||||
|
||||
private bool recursive = false;
|
||||
/// <summary>Whether to parse multiple values that match the criteria or just the first one.</summary>
|
||||
public bool Recursive { get { return recursive; } set { recursive = value; OnPropertyChanged(); } }
|
||||
|
||||
private ParseType type = ParseType.LR;
|
||||
/// <summary>The parsing algorithm being used.</summary>
|
||||
public ParseType Type { get { return type; } set { type = value; OnPropertyChanged(); } }
|
||||
|
||||
#region LR
|
||||
private string leftString = "";
|
||||
/// <summary>The string to the immediate left of the text to parse. An empty string means the start of the input.</summary>
|
||||
public string LeftString { get { return leftString; } set { leftString = value; OnPropertyChanged(); } }
|
||||
|
||||
private string rightString = "";
|
||||
/// <summary>The string to the immediate right of the text to parse. An empty string means the end of the input.</summary>
|
||||
public string RightString { get { return rightString; } set { rightString = value; OnPropertyChanged(); } }
|
||||
|
||||
private bool useRegexLR = false;
|
||||
/// <summary>Whether to use a regex pattern to match a text between two strings instead of the standard algorithm.</summary>
|
||||
public bool UseRegexLR { get { return useRegexLR; } set { useRegexLR = value; OnPropertyChanged(); } }
|
||||
#endregion
|
||||
|
||||
#region CSS
|
||||
private string cssSelector = "";
|
||||
/// <summary>The CSS selector that addresses the desired element in the HTML page.</summary>
|
||||
public string CssSelector { get { return cssSelector; } set { cssSelector = value; OnPropertyChanged(); } }
|
||||
|
||||
private string attributeName = "";
|
||||
/// <summary>The name of the attribute from which to parse the value.</summary>
|
||||
public string AttributeName { get { return attributeName; } set { attributeName = value; OnPropertyChanged(); } }
|
||||
|
||||
private int cssElementIndex = 0;
|
||||
/// <summary>The index of the desired element when the selector matches multiple elements.</summary>
|
||||
public int CssElementIndex { get { return cssElementIndex; } set { cssElementIndex = value; OnPropertyChanged(); } }
|
||||
#endregion
|
||||
|
||||
#region JSON
|
||||
private string jsonField = "";
|
||||
/// <summary>The name of the json field for which we want to retrieve the value.</summary>
|
||||
public string JsonField { get { return jsonField; } set { jsonField = value; OnPropertyChanged(); } }
|
||||
#endregion
|
||||
|
||||
#region REGEX
|
||||
private string regexString = "";
|
||||
/// <summary>The regex pattern that matches parts of the text inside groups.</summary>
|
||||
public string RegexString { get { return regexString; } set { regexString = value; OnPropertyChanged(); } }
|
||||
|
||||
private string regexOutput = "";
|
||||
/// <summary>The way the content of the matched groups should be formatted. [0] will be replaced with the full match, [1] with the first group etc.</summary>
|
||||
public string RegexOutput { get { return regexOutput; } set { regexOutput = value; OnPropertyChanged(); } }
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Creates a Parse block.
|
||||
/// </summary>
|
||||
public BlockParse()
|
||||
{
|
||||
Label = "PARSE";
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override BlockBase FromLS(string line)
|
||||
{
|
||||
// Trim the line
|
||||
var input = line.Trim();
|
||||
|
||||
// Parse the label
|
||||
if (input.StartsWith("#"))
|
||||
Label = LineParser.ParseLabel(ref input);
|
||||
|
||||
ParseTarget = LineParser.ParseLiteral(ref input, "TARGET");
|
||||
|
||||
Type = (ParseType)LineParser.ParseEnum(ref input, "TYPE", typeof(ParseType));
|
||||
|
||||
switch (Type)
|
||||
{
|
||||
case ParseType.LR:
|
||||
// PARSE "<SOURCE>" LR "L" "R" RECURSIVE? -> VAR/CAP "ABC"
|
||||
LeftString = LineParser.ParseLiteral(ref input, "LEFT STRING");
|
||||
RightString = LineParser.ParseLiteral(ref input, "RIGHT STRING");
|
||||
if (LineParser.Lookahead(ref input) == TokenType.Boolean)
|
||||
LineParser.SetBool(ref input, this);
|
||||
break;
|
||||
|
||||
case ParseType.CSS:
|
||||
// PARSE "<SOURCE>" CSS "Selector" "Attribute" Index RECURSIVE? ->
|
||||
CssSelector = LineParser.ParseLiteral(ref input, "SELECTOR");
|
||||
AttributeName = LineParser.ParseLiteral(ref input, "ATTRIBUTE");
|
||||
if (LineParser.Lookahead(ref input) == TokenType.Boolean)
|
||||
LineParser.SetBool(ref input, this);
|
||||
else if (LineParser.Lookahead(ref input) == TokenType.Integer)
|
||||
CssElementIndex = LineParser.ParseInt(ref input, "INDEX");
|
||||
break;
|
||||
|
||||
case ParseType.JSON:
|
||||
// PARSE "<SOURCE>" JSON "Field" ->
|
||||
JsonField = LineParser.ParseLiteral(ref input, "FIELD");
|
||||
if (LineParser.Lookahead(ref input) == TokenType.Boolean)
|
||||
LineParser.SetBool(ref input, this);
|
||||
break;
|
||||
|
||||
case ParseType.REGEX:
|
||||
// PARSE "<SOURCE>" REGEX "Pattern" "Output" RECURSIVE? ->
|
||||
RegexString = LineParser.ParseLiteral(ref input, "PATTERN");
|
||||
RegexOutput = LineParser.ParseLiteral(ref input, "OUTPUT");
|
||||
if (LineParser.Lookahead(ref input) == TokenType.Boolean)
|
||||
LineParser.SetBool(ref input, this);
|
||||
break;
|
||||
}
|
||||
|
||||
// Parse the arrow
|
||||
LineParser.ParseToken(ref input, TokenType.Arrow, true);
|
||||
|
||||
// Parse the VAR / CAP
|
||||
try
|
||||
{
|
||||
var varType = LineParser.ParseToken(ref input, TokenType.Parameter, true);
|
||||
if (varType.ToUpper() == "VAR" || varType.ToUpper() == "CAP")
|
||||
IsCapture = varType.ToUpper() == "CAP";
|
||||
}
|
||||
catch { throw new ArgumentException("Invalid or missing variable type"); }
|
||||
|
||||
// Parse the variable/capture name
|
||||
try { VariableName = LineParser.ParseLiteral(ref input, "NAME"); }
|
||||
catch { throw new ArgumentException("Variable name not specified"); }
|
||||
|
||||
// Parse the prefix and suffix
|
||||
if (LineParser.ParseToken(ref input, TokenType.Literal, false, false) == "")
|
||||
return this;
|
||||
|
||||
Prefix = LineParser.ParseLiteral(ref input, "PREFIX");
|
||||
Suffix = LineParser.ParseLiteral(ref input, "SUFFIX");
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToLS(bool indent = true)
|
||||
{
|
||||
var writer = new BlockWriter(GetType(), indent, Disabled);
|
||||
writer
|
||||
.Label(Label)
|
||||
.Token("PARSE")
|
||||
.Literal(ParseTarget)
|
||||
.Token(Type);
|
||||
|
||||
switch (Type)
|
||||
{
|
||||
case ParseType.LR:
|
||||
writer.Literal(LeftString).Literal(RightString).Boolean(Recursive, "Recursive");
|
||||
break;
|
||||
|
||||
case ParseType.CSS:
|
||||
writer.Literal(CssSelector).Literal(AttributeName);
|
||||
if (Recursive) writer.Boolean(Recursive, "Recursive");
|
||||
else writer.Integer(CssElementIndex, "CssElementIndex");
|
||||
break;
|
||||
|
||||
case ParseType.JSON:
|
||||
writer.Literal(JsonField).Boolean(Recursive, "Recursive");
|
||||
break;
|
||||
|
||||
case ParseType.REGEX:
|
||||
writer.Literal(RegexString).Literal(RegexOutput).Boolean(Recursive, "Recursive");
|
||||
break;
|
||||
}
|
||||
|
||||
writer
|
||||
.Arrow()
|
||||
.Token(IsCapture ? "CAP" : "VAR")
|
||||
.Literal(VariableName);
|
||||
|
||||
if (!writer.CheckDefault(Prefix, "Prefix") || !writer.CheckDefault(Suffix, "Suffix"))
|
||||
writer.Literal(Prefix).Literal(Suffix);
|
||||
|
||||
return writer.ToString();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Process(BotData data)
|
||||
{
|
||||
base.Process(data);
|
||||
|
||||
InsertVariables(data, isCapture, recursive, Parse(data), variableName, prefix, suffix);
|
||||
}
|
||||
|
||||
private List<string> Parse(BotData data)
|
||||
{
|
||||
var original = ReplaceValues(parseTarget, data);
|
||||
var partial = original;
|
||||
var list = new List<string>();
|
||||
|
||||
// Parse the value
|
||||
switch (Type)
|
||||
{
|
||||
case ParseType.LR:
|
||||
var ls = ReplaceValues(leftString, data);
|
||||
var rs = ReplaceValues(rightString, data);
|
||||
var pFrom = 0;
|
||||
var pTo = 0;
|
||||
|
||||
// No L and R = return full input
|
||||
if (ls == "" && rs == "")
|
||||
{
|
||||
list.Add(original);
|
||||
break;
|
||||
}
|
||||
|
||||
// L or R not present and not empty
|
||||
else if ( ((!partial.Contains(ls) && ls != "") || (!partial.Contains(rs) && rs != "")))
|
||||
{
|
||||
list.Add("");
|
||||
break;
|
||||
}
|
||||
|
||||
// Instead of the mess below, we could simply use Extreme.NET's Substring extensions
|
||||
// return original.Substrings(ls, rs); // Recursive
|
||||
// return original.Substring(ls, rs); // Not recursive
|
||||
|
||||
if (recursive)
|
||||
{
|
||||
if (useRegexLR)
|
||||
{
|
||||
try
|
||||
{
|
||||
var pattern = BuildLRPattern(ls, rs);
|
||||
MatchCollection mc = Regex.Matches(partial, pattern);
|
||||
foreach (Match m in mc)
|
||||
list.Add(m.Value);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
while ((partial.Contains(ls) || ls == "") && (partial.Contains(rs) || rs == ""))
|
||||
{
|
||||
// Search for left delimiter and Calculate offset
|
||||
pFrom = ls == "" ? 0 : partial.IndexOf(ls) + ls.Length;
|
||||
// Move right of offset
|
||||
partial = partial.Substring(pFrom);
|
||||
// Search for right delimiter and Calculate length to parse
|
||||
pTo = rs == "" ? (partial.Length - 1) : partial.IndexOf(rs);
|
||||
// Parse it
|
||||
var parsed = partial.Substring(0, pTo);
|
||||
list.Add(parsed);
|
||||
// Move right of parsed + right
|
||||
partial = partial.Substring(parsed.Length + rs.Length);
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
|
||||
// Non-recursive
|
||||
else
|
||||
{
|
||||
if (useRegexLR)
|
||||
{
|
||||
var pattern = BuildLRPattern(ls, rs);
|
||||
MatchCollection mc = Regex.Matches(partial, pattern);
|
||||
if (mc.Count > 0) list.Add(mc[0].Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
pFrom = ls == "" ? 0 : partial.IndexOf(ls) + ls.Length;
|
||||
partial = partial.Substring(pFrom);
|
||||
pTo = rs == "" ? (partial.Length - 1) : partial.IndexOf(rs);
|
||||
list.Add(partial.Substring(0, pTo));
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case ParseType.CSS:
|
||||
|
||||
HtmlParser parser = new HtmlParser();
|
||||
AngleSharp.Dom.Html.IHtmlDocument document = null;
|
||||
try { document = parser.Parse(data.ResponseSource); } catch { list.Add(""); }
|
||||
|
||||
try
|
||||
{
|
||||
if (recursive)
|
||||
{
|
||||
foreach(var element in document.QuerySelectorAll(ReplaceValues(cssSelector,data)))
|
||||
{
|
||||
switch (ReplaceValues(attributeName, data))
|
||||
{
|
||||
case "innerHTML":
|
||||
list.Add(element.InnerHtml);
|
||||
break;
|
||||
case "outerHTML":
|
||||
list.Add(element.OuterHtml);
|
||||
break;
|
||||
default:
|
||||
foreach(var attr in element.Attributes)
|
||||
{
|
||||
if(attr.Name == ReplaceValues(attributeName,data))
|
||||
{
|
||||
list.Add(attr.Value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (ReplaceValues(attributeName, data))
|
||||
{
|
||||
case "innerHTML":
|
||||
list.Add(document.QuerySelectorAll(ReplaceValues(cssSelector, data))[cssElementIndex].InnerHtml);
|
||||
break;
|
||||
|
||||
case "outerHTML":
|
||||
list.Add(document.QuerySelectorAll(ReplaceValues(cssSelector, data))[cssElementIndex].OuterHtml);
|
||||
break;
|
||||
|
||||
default:
|
||||
foreach (var attr in document.QuerySelectorAll(ReplaceValues(cssSelector, data))[cssElementIndex].Attributes)
|
||||
{
|
||||
if (attr.Name == ReplaceValues(attributeName, data))
|
||||
{
|
||||
list.Add(attr.Value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch { list.Add(""); }
|
||||
|
||||
break;
|
||||
|
||||
case ParseType.JSON:
|
||||
var jsonlist = new List<KeyValuePair<string, string>>();
|
||||
parseJSON("", original, jsonlist);
|
||||
foreach(var j in jsonlist)
|
||||
if (j.Key == ReplaceValues(jsonField, data))
|
||||
list.Add(j.Value);
|
||||
break;
|
||||
|
||||
case ParseType.XPATH:
|
||||
|
||||
// NOT IMPLEMENTED YET
|
||||
break;
|
||||
|
||||
case ParseType.REGEX:
|
||||
REGEXBEGIN:
|
||||
try
|
||||
{
|
||||
var matches = Regex.Matches(partial, ReplaceValues(regexString, data));
|
||||
foreach(Match match in matches)
|
||||
{
|
||||
var output = ReplaceValues(regexOutput, data);
|
||||
for (var i = 0; i < match.Groups.Count; i++) output = output.Replace("[" + i + "]", match.Groups[i].Value);
|
||||
list.Add(output);
|
||||
if (recursive && match.Index + match.Length <= partial.Length) { partial = partial.Substring(match.Index + match.Length); goto REGEXBEGIN; }
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
break;
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
private string BuildLRPattern(string ls, string rs)
|
||||
{
|
||||
var left = string.IsNullOrEmpty(ls) ? "^" : Regex.Escape(ls); // Empty LEFT = start of the line
|
||||
var right = string.IsNullOrEmpty(rs) ? "$" : Regex.Escape(rs); // Empty RIGHT = end of the line
|
||||
return "(?<=" + left + ").+?(?=" + right + ")";
|
||||
}
|
||||
|
||||
private static void parseJSON(string A, string B, List<KeyValuePair<string, string>> jsonlist)
|
||||
{
|
||||
jsonlist.Add(new KeyValuePair<string, string>(A, B));
|
||||
|
||||
if (B.StartsWith("["))
|
||||
{
|
||||
JArray arr = null;
|
||||
try { arr = JArray.Parse(B); } catch { return; }
|
||||
|
||||
foreach (var i in arr.Children())
|
||||
parseJSON("", i.ToString(), jsonlist);
|
||||
}
|
||||
|
||||
if (B.Contains("{"))
|
||||
{
|
||||
JObject obj = null;
|
||||
try { obj = JObject.Parse(B); } catch { return; }
|
||||
|
||||
foreach (var o in obj)
|
||||
parseJSON(o.Key, o.Value.ToString(), jsonlist);
|
||||
}
|
||||
}
|
||||
|
||||
private string cleanString(string inputString)
|
||||
{
|
||||
return Regex.Replace(inputString, "<br>", "").Trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
using RuriLib.CaptchaServices;
|
||||
using RuriLib.LS;
|
||||
using RuriLib.Models;
|
||||
using System;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace RuriLib
|
||||
{
|
||||
/// <summary>
|
||||
/// A block that solves a reCaptcha challenge.
|
||||
/// </summary>
|
||||
public class BlockRecaptcha : BlockCaptcha
|
||||
{
|
||||
private string variableName = "";
|
||||
/// <summary>The name of the output variable where the challenge solution will be stored.</summary>
|
||||
public string VariableName { get { return variableName; } set { variableName = value; OnPropertyChanged(); } }
|
||||
|
||||
private string url = "https://google.com";
|
||||
/// <summary>The URL where the reCaptcha challenge appears.</summary>
|
||||
public string Url { get { return url; } set { url = value; OnPropertyChanged(); } }
|
||||
|
||||
private string siteKey = "";
|
||||
/// <summary>The Google SiteKey found in the page's source code.</summary>
|
||||
public string SiteKey { get { return siteKey; } set { siteKey = value; OnPropertyChanged(); } }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a reCaptcha block.
|
||||
/// </summary>
|
||||
public BlockRecaptcha()
|
||||
{
|
||||
Label = "RECAPTCHA";
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override BlockBase FromLS(string line)
|
||||
{
|
||||
// Trim the line
|
||||
var input = line.Trim();
|
||||
|
||||
// Parse the label
|
||||
if (input.StartsWith("#"))
|
||||
Label = LineParser.ParseLabel(ref input);
|
||||
|
||||
/*
|
||||
* Syntax:
|
||||
* RECAPTCHA "URL" "SITEKEY"->VAR "RECAP"
|
||||
* */
|
||||
|
||||
Url = LineParser.ParseLiteral(ref input, "URL");
|
||||
SiteKey = LineParser.ParseLiteral(ref input, "SITEKEY");
|
||||
|
||||
if (LineParser.ParseToken(ref input, TokenType.Arrow, false) == "")
|
||||
return this;
|
||||
|
||||
LineParser.EnsureIdentifier(ref input, "VAR");
|
||||
|
||||
// Parse the variable name
|
||||
VariableName = LineParser.ParseLiteral(ref input, "VARIABLE NAME");
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToLS(bool indent = true)
|
||||
{
|
||||
var writer = new BlockWriter(GetType(), indent, Disabled);
|
||||
writer
|
||||
.Label(Label)
|
||||
.Token("RECAPTCHA")
|
||||
.Literal(Url)
|
||||
.Literal(SiteKey)
|
||||
.Arrow()
|
||||
.Token("VAR")
|
||||
.Literal(VariableName);
|
||||
return writer.ToString();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Process(BotData data)
|
||||
{
|
||||
if(!data.GlobalSettings.Captchas.BypassBalanceCheck)
|
||||
base.Process(data);
|
||||
|
||||
data.Log(new LogEntry("Solving reCaptcha...", Colors.White));
|
||||
|
||||
string recapResponse = "";
|
||||
CaptchaServices.CaptchaService service = null;
|
||||
|
||||
switch (data.GlobalSettings.Captchas.CurrentService)
|
||||
{
|
||||
case CaptchaService.ImageTypers:
|
||||
service = new ImageTyperz(data.GlobalSettings.Captchas.ImageTypToken, data.GlobalSettings.Captchas.Timeout);
|
||||
break;
|
||||
|
||||
case CaptchaService.AntiCaptcha:
|
||||
service = new AntiCaptcha(data.GlobalSettings.Captchas.AntiCapToken, data.GlobalSettings.Captchas.Timeout);
|
||||
break;
|
||||
|
||||
case CaptchaService.DBC:
|
||||
service = new DeathByCaptcha(data.GlobalSettings.Captchas.DBCUser, data.GlobalSettings.Captchas.DBCPass, data.GlobalSettings.Captchas.Timeout);
|
||||
break;
|
||||
|
||||
case CaptchaService.TwoCaptcha:
|
||||
service = new TwoCaptcha(data.GlobalSettings.Captchas.TwoCapToken, data.GlobalSettings.Captchas.Timeout);
|
||||
break;
|
||||
|
||||
case CaptchaService.DeCaptcher:
|
||||
service = new DeCaptcher(data.GlobalSettings.Captchas.DCUser, data.GlobalSettings.Captchas.DCPass, data.GlobalSettings.Captchas.Timeout);
|
||||
break;
|
||||
|
||||
case CaptchaService.AZCaptcha:
|
||||
service = new AZCaptcha(data.GlobalSettings.Captchas.AZCapToken, data.GlobalSettings.Captchas.Timeout);
|
||||
break;
|
||||
|
||||
case CaptchaService.SolveRecaptcha:
|
||||
service = new SolveReCaptcha(data.GlobalSettings.Captchas.SRUserId, data.GlobalSettings.Captchas.SRToken, data.GlobalSettings.Captchas.Timeout);
|
||||
break;
|
||||
|
||||
case CaptchaService.CaptchasIO:
|
||||
service = new CaptchasIO(data.GlobalSettings.Captchas.CIOToken, data.GlobalSettings.Captchas.Timeout);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Exception("This service cannot solve reCaptchas!");
|
||||
}
|
||||
recapResponse = service.SolveRecaptcha(siteKey, ReplaceValues(url, data));
|
||||
|
||||
data.Log(recapResponse == "" ? new LogEntry("Couldn't get a reCaptcha response from the service", Colors.Tomato) : new LogEntry("Succesfully got the response: " + recapResponse, Colors.GreenYellow));
|
||||
if (VariableName != "")
|
||||
{
|
||||
data.Log(new LogEntry("Response stored in variable: " + variableName, Colors.White));
|
||||
data.Variables.Set(new CVar(variableName, recapResponse));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,650 @@
|
||||
using Extreme.Net;
|
||||
using RuriLib.LS;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace RuriLib
|
||||
{
|
||||
/// <summary>
|
||||
/// The types of request that can be performed.
|
||||
/// </summary>
|
||||
public enum RequestType
|
||||
{
|
||||
/// <summary>A standard request with standard content.</summary>
|
||||
Standard,
|
||||
|
||||
/// <summary>A request which uses the 'Authentication: Basic' header.</summary>
|
||||
BasicAuth,
|
||||
|
||||
/// <summary>A request which contains multipart content (strings and/or files).</summary>
|
||||
Multipart
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The available types of multipart contents.
|
||||
/// </summary>
|
||||
public enum MultipartContentType
|
||||
{
|
||||
/// <summary>A string content.</summary>
|
||||
String,
|
||||
|
||||
/// <summary>A file content.</summary>
|
||||
File
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The type of data expected inside the HTTP response.
|
||||
/// </summary>
|
||||
public enum ResponseType
|
||||
{
|
||||
/// <summary>A string response, e.g. an HTML page.</summary>
|
||||
String,
|
||||
|
||||
/// <summary>A file response, e.g. an image.</summary>
|
||||
File
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A block that can perform HTTP requests.
|
||||
/// </summary>
|
||||
public class BlockRequest : BlockBase
|
||||
{
|
||||
#region Variables
|
||||
private string url = "https://google.com";
|
||||
/// <summary>The URL to call, including additional GET query parameters.</summary>
|
||||
public string Url { get { return url; } set { url = value; OnPropertyChanged(); } }
|
||||
|
||||
private RequestType requestType = RequestType.Standard;
|
||||
/// <summary>The request type.</summary>
|
||||
public RequestType RequestType { get { return requestType; } set { requestType = value; OnPropertyChanged(); } }
|
||||
|
||||
// Basic Auth
|
||||
private string authUser = "";
|
||||
/// <summary>The username for basic auth requests.</summary>
|
||||
public string AuthUser { get { return authUser; } set { authUser = value; OnPropertyChanged(); } }
|
||||
|
||||
private string authPass = "";
|
||||
/// <summary>The password for basic auth requests.</summary>
|
||||
public string AuthPass { get { return authPass; } set { authPass = value; OnPropertyChanged(); } }
|
||||
|
||||
// Standard
|
||||
private string postData = "";
|
||||
/// <summary>The content of the request, sent after the headers. Use '\n' to input a linebreak.</summary>
|
||||
public string PostData { get { return postData; } set { postData = value; OnPropertyChanged(); } }
|
||||
|
||||
private HttpMethod method = HttpMethod.GET;
|
||||
/// <summary>The method of the HTTP request.</summary>
|
||||
public HttpMethod Method { get { return method; } set { method = value; OnPropertyChanged(); } }
|
||||
|
||||
/// <summary>The custom headers that are sent in the HTTP request.</summary>
|
||||
public Dictionary<string, string> CustomHeaders { get; set; } = new Dictionary<string, string>() {
|
||||
{ "User-Agent", "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko" },
|
||||
{ "Pragma", "no-cache" },
|
||||
{ "Accept", "*/*" }
|
||||
};
|
||||
|
||||
/// <summary>The custom cookies that are sent in the HTTP request.</summary>
|
||||
public Dictionary<string, string> CustomCookies { get; set; } = new Dictionary<string, string>() { };
|
||||
|
||||
private string contentType = "application/x-www-form-urlencoded";
|
||||
/// <summary>The type of content the server should expect.</summary>
|
||||
public string ContentType { get { return contentType; } set { contentType = value; OnPropertyChanged(); } }
|
||||
|
||||
private bool autoRedirect = true;
|
||||
/// <summary>Whether to perform automatic redirection in the case of 3xx headers.</summary>
|
||||
public bool AutoRedirect { get { return autoRedirect; } set { autoRedirect = value; OnPropertyChanged(); } }
|
||||
|
||||
private bool readResponseSource = true;
|
||||
/// <summary>Whether to read the stream of data from the HTTP response. Set to false if only the headers are needed, in order to speed up the process.</summary>
|
||||
public bool ReadResponseSource { get { return readResponseSource; } set { readResponseSource = value; OnPropertyChanged(); } }
|
||||
|
||||
// Multipart
|
||||
private string multipartBoundary = "";
|
||||
/// <summary>The boundary that separates multipart contents.</summary>
|
||||
public string MultipartBoundary { get { return multipartBoundary; } set { multipartBoundary = value; OnPropertyChanged(); } }
|
||||
|
||||
/// <summary>The list of contents to send in a multipart request.</summary>
|
||||
public List<MultipartContent> MultipartContents { get; set; } = new List<MultipartContent>();
|
||||
|
||||
private ResponseType responseType = ResponseType.String;
|
||||
/// <summary>The type of response expected from the server.</summary>
|
||||
public ResponseType ResponseType { get { return responseType; } set { responseType = value; OnPropertyChanged(); } }
|
||||
|
||||
private string downloadPath = "";
|
||||
/// <summary>The path of the file where a FILE response needs to be stored.</summary>
|
||||
public string DownloadPath { get { return downloadPath; } set { downloadPath = value; OnPropertyChanged(); } }
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Creates a Request block.
|
||||
/// </summary>
|
||||
public BlockRequest()
|
||||
{
|
||||
Label = "REQUEST";
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override BlockBase FromLS(string line)
|
||||
{
|
||||
// Trim the line
|
||||
var input = line.Trim();
|
||||
|
||||
// Parse the label
|
||||
if (input.StartsWith("#"))
|
||||
Label = LineParser.ParseLabel(ref input);
|
||||
|
||||
Method = (HttpMethod)LineParser.ParseEnum(ref input, "METHOD", typeof(HttpMethod));
|
||||
Url = LineParser.ParseLiteral(ref input, "URL");
|
||||
|
||||
while (LineParser.Lookahead(ref input) == TokenType.Boolean)
|
||||
LineParser.SetBool(ref input, this);
|
||||
|
||||
while (input != "" && !input.StartsWith("->"))
|
||||
{
|
||||
var parsed = LineParser.ParseToken(ref input, TokenType.Parameter, true).ToUpper();
|
||||
switch (parsed)
|
||||
{
|
||||
case "MULTIPART":
|
||||
RequestType = RequestType.Multipart;
|
||||
break;
|
||||
|
||||
case "BASICAUTH":
|
||||
RequestType = RequestType.BasicAuth;
|
||||
break;
|
||||
|
||||
case "STANDARD":
|
||||
RequestType = RequestType.Standard;
|
||||
break;
|
||||
|
||||
case "CONTENT":
|
||||
PostData = LineParser.ParseLiteral(ref input, "POST DATA");
|
||||
break;
|
||||
|
||||
case "STRINGCONTENT":
|
||||
var sCont = LineParser.ParseLiteral(ref input, "STRING CONTENT");
|
||||
var sSplit = sCont.Split(new char[] { ':' }, 2);
|
||||
MultipartContents.Add(new MultipartContent() { Type = MultipartContentType.String, Name = sSplit[0].Trim(), Value = sSplit[1].Trim() });
|
||||
break;
|
||||
|
||||
case "FILECONTENT":
|
||||
var fCont = LineParser.ParseLiteral(ref input, "FILE CONTENT");
|
||||
var fSplit = fCont.Split(new char[] { ':' }, 2);
|
||||
MultipartContents.Add(new MultipartContent() { Type = MultipartContentType.File, Name = fSplit[0].Trim(), Value = fSplit[1].Trim() });
|
||||
break;
|
||||
|
||||
case "COOKIE":
|
||||
var cookiePair = ParsePair(LineParser.ParseLiteral(ref input, "COOKIE VALUE"));
|
||||
CustomCookies[cookiePair.Key] = cookiePair.Value;
|
||||
break;
|
||||
|
||||
case "HEADER":
|
||||
var headerPair = ParsePair(LineParser.ParseLiteral(ref input, "HEADER VALUE"));
|
||||
CustomHeaders[headerPair.Key] = headerPair.Value;
|
||||
break;
|
||||
|
||||
case "CONTENTTYPE":
|
||||
ContentType = LineParser.ParseLiteral(ref input, "CONTENT TYPE");
|
||||
break;
|
||||
|
||||
case "USERNAME":
|
||||
AuthUser = LineParser.ParseLiteral(ref input, "USERNAME");
|
||||
break;
|
||||
|
||||
case "PASSWORD":
|
||||
AuthPass = LineParser.ParseLiteral(ref input, "PASSWORD");
|
||||
break;
|
||||
|
||||
case "BOUNDARY":
|
||||
MultipartBoundary = LineParser.ParseLiteral(ref input, "BOUNDARY");
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (input.StartsWith("->"))
|
||||
{
|
||||
LineParser.EnsureIdentifier(ref input, "->");
|
||||
var outType = LineParser.ParseToken(ref input, TokenType.Parameter, true);
|
||||
if (outType.ToUpper() == "STRING") ResponseType = ResponseType.String;
|
||||
else if (outType.ToUpper() == "FILE")
|
||||
{
|
||||
ResponseType = ResponseType.File;
|
||||
DownloadPath = LineParser.ParseLiteral(ref input, "DOWNLOAD PATH");
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses a pair of values separated by a colon.
|
||||
/// </summary>
|
||||
/// <param name="pair">The string containing colon-separated values</param>
|
||||
/// <returns>The pair of values.</returns>
|
||||
public static KeyValuePair<string, string> ParsePair(string pair)
|
||||
{
|
||||
var split = pair.Split(new[] { ':' }, 2);
|
||||
return new KeyValuePair<string, string>(split[0].Trim(), split[1].Trim());
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToLS(bool indent = true)
|
||||
{
|
||||
var writer = new BlockWriter(GetType(), indent, Disabled);
|
||||
writer
|
||||
.Label(Label)
|
||||
.Token("REQUEST")
|
||||
.Token(Method)
|
||||
.Literal(Url)
|
||||
.Boolean(AutoRedirect, "AutoRedirect")
|
||||
.Boolean(ReadResponseSource, "ReadResponseSource")
|
||||
.Token(RequestType, "RequestType")
|
||||
.Indent();
|
||||
|
||||
switch (RequestType)
|
||||
{
|
||||
case RequestType.BasicAuth:
|
||||
writer
|
||||
.Token("USERNAME")
|
||||
.Literal(AuthUser)
|
||||
.Token("PASSWORD")
|
||||
.Literal(AuthPass)
|
||||
.Indent();
|
||||
break;
|
||||
|
||||
case RequestType.Standard:
|
||||
if (!writer.CheckDefault(PostData, "PostData"))
|
||||
{
|
||||
writer
|
||||
.Token("CONTENT")
|
||||
.Literal(PostData)
|
||||
.Indent()
|
||||
.Token("CONTENTTYPE")
|
||||
.Literal(ContentType);
|
||||
}
|
||||
break;
|
||||
|
||||
case RequestType.Multipart:
|
||||
foreach(var c in MultipartContents)
|
||||
{
|
||||
writer
|
||||
.Indent()
|
||||
.Token($"{c.Type.ToString().ToUpper()}CONTENT")
|
||||
.Literal($"{c.Name}: {c.Value}");
|
||||
}
|
||||
if (!writer.CheckDefault(MultipartBoundary, "MultipartBoundary"))
|
||||
{
|
||||
writer
|
||||
.Indent()
|
||||
.Token("BOUNDARY")
|
||||
.Literal(MultipartBoundary);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
foreach (var c in CustomCookies)
|
||||
{
|
||||
writer
|
||||
.Indent()
|
||||
.Token("COOKIE")
|
||||
.Literal($"{c.Key}: {c.Value}");
|
||||
}
|
||||
|
||||
foreach (var h in CustomHeaders)
|
||||
{
|
||||
writer
|
||||
.Indent()
|
||||
.Token("HEADER")
|
||||
.Literal($"{h.Key}: {h.Value}");
|
||||
}
|
||||
|
||||
if (ResponseType == ResponseType.File)
|
||||
{
|
||||
writer
|
||||
.Indent()
|
||||
.Arrow()
|
||||
.Token("FILE")
|
||||
.Literal(DownloadPath);
|
||||
}
|
||||
|
||||
return writer.ToString();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Process(BotData data)
|
||||
{
|
||||
base.Process(data);
|
||||
|
||||
#region Request
|
||||
// Set base URL
|
||||
var localUrl = ReplaceValues(url, data);
|
||||
var cType = ReplaceValues(contentType, data);
|
||||
|
||||
// Create request
|
||||
HttpRequest request = new HttpRequest();
|
||||
|
||||
// Setup options
|
||||
var timeout = data.GlobalSettings.General.RequestTimeout * 1000;
|
||||
request.IgnoreProtocolErrors = true;
|
||||
request.AllowAutoRedirect = autoRedirect;
|
||||
request.EnableEncodingContent = true;
|
||||
request.ReadWriteTimeout = timeout;
|
||||
request.ConnectTimeout = timeout;
|
||||
request.KeepAlive = true;
|
||||
|
||||
data.Log(new LogEntry(string.Format("Calling URL: {0}", localUrl), Colors.MediumTurquoise));
|
||||
|
||||
// Set up the Content and Content-Type
|
||||
HttpContent content = null;
|
||||
switch (requestType)
|
||||
{
|
||||
case RequestType.Standard:
|
||||
var pData = string.Join(Environment.NewLine, postData
|
||||
.Split(new string[] { "\\n" }, StringSplitOptions.None)
|
||||
.Select(p => ReplaceValues(p, data)));
|
||||
content = new StringContent(pData);
|
||||
content.ContentType = cType;
|
||||
data.Log(new LogEntry(string.Format("Post Data: {0}", pData), Colors.MediumTurquoise));
|
||||
break;
|
||||
|
||||
case RequestType.Multipart:
|
||||
if (multipartBoundary != "") content = new Extreme.Net.MultipartContent(multipartBoundary);
|
||||
else content = new Extreme.Net.MultipartContent(GenerateMultipartBoundary());
|
||||
var mContent = content as Extreme.Net.MultipartContent;
|
||||
foreach (var c in MultipartContents)
|
||||
{
|
||||
if (c.Type == MultipartContentType.String) mContent.Add(new StringContent(ReplaceValues(c.Value, data)), ReplaceValues(c.Name, data));
|
||||
else if (c.Type == MultipartContentType.File) mContent.Add(new FileContent(ReplaceValues(c.Value, data)), ReplaceValues(c.Name, data));
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Set proxy
|
||||
if (data.UseProxies)
|
||||
{
|
||||
request.Proxy = data.Proxy.GetClient();
|
||||
|
||||
try
|
||||
{
|
||||
request.Proxy.ReadWriteTimeout = timeout;
|
||||
request.Proxy.ConnectTimeout = timeout;
|
||||
request.Proxy.Username = data.Proxy.Username;
|
||||
request.Proxy.Password = data.Proxy.Password;
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
// Set headers
|
||||
data.Log(new LogEntry("Sent Headers:", Colors.DarkTurquoise));
|
||||
var fixedNames = Enum.GetNames(typeof(HttpHeader)).Select(n => n.ToLower());
|
||||
foreach (var header in CustomHeaders)
|
||||
{
|
||||
try
|
||||
{
|
||||
var key = ReplaceValues(header.Key, data);
|
||||
var replacedKey = key.Replace("-", "").ToLower(); // Used to compare with the HttpHeader enum
|
||||
var val = ReplaceValues(header.Value, data);
|
||||
|
||||
if (fixedNames.Contains(replacedKey)) request.AddHeader((HttpHeader)Enum.Parse(typeof(HttpHeader), replacedKey, true), val);
|
||||
else request.AddHeader(key, val);
|
||||
|
||||
data.Log(new LogEntry(key + ": " + val, Colors.MediumTurquoise));
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
// Add the authorization header on a Basic Auth request
|
||||
if (requestType == RequestType.BasicAuth)
|
||||
{
|
||||
var usr = ReplaceValues(authUser, data);
|
||||
var pwd = ReplaceValues(authPass, data);
|
||||
var auth = "Basic " + BlockFunction.Base64Encode(usr + ":" + pwd);
|
||||
request.AddHeader("Authorization", auth);
|
||||
data.Log(new LogEntry("Authorization: " + auth, Colors.MediumTurquoise));
|
||||
}
|
||||
|
||||
// Add the content-type header
|
||||
if ((method == HttpMethod.POST || method == HttpMethod.PUT || method == HttpMethod.DELETE) && cType != "")
|
||||
data.Log(new LogEntry("Content-Type: " + cType, Colors.MediumTurquoise));
|
||||
|
||||
// Add new user-defined custom cookies to the bot's cookie jar
|
||||
request.Cookies = new CookieDictionary();
|
||||
foreach (var cookie in CustomCookies)
|
||||
data.Cookies[ReplaceValues(cookie.Key, data)] = ReplaceValues(cookie.Value, data);
|
||||
|
||||
// Set cookies from the bot's cookie jar to the request's CookieDictionary
|
||||
data.Log(new LogEntry("Sent Cookies:", Colors.MediumTurquoise));
|
||||
foreach (var cookie in data.Cookies)
|
||||
{
|
||||
request.Cookies.Add(cookie.Key, cookie.Value);
|
||||
data.Log(new LogEntry(cookie.Key + " : " + cookie.Value, Colors.MediumTurquoise));
|
||||
}
|
||||
|
||||
data.LogNewLine();
|
||||
#endregion
|
||||
|
||||
#region Response
|
||||
// Create the response
|
||||
HttpResponse response = null;
|
||||
|
||||
try
|
||||
{
|
||||
// Get response
|
||||
response = request.Raw(method, localUrl, content);
|
||||
|
||||
// Get address
|
||||
data.Address = response.Address.ToString();
|
||||
data.Log(new LogEntry("Address: " + data.Address, Colors.Cyan));
|
||||
|
||||
// Get code
|
||||
data.ResponseCode = ((int)response.StatusCode).ToString();
|
||||
data.Log(new LogEntry("Response code: " + data.ResponseCode, Colors.Cyan));
|
||||
|
||||
// Get headers
|
||||
data.Log(new LogEntry("Received headers:", Colors.DeepPink));
|
||||
var headerList = new List<KeyValuePair<string, string>>();
|
||||
var receivedHeaders = response.EnumerateHeaders();
|
||||
data.ResponseHeaders.Clear();
|
||||
while (receivedHeaders.MoveNext())
|
||||
{
|
||||
var header = receivedHeaders.Current;
|
||||
data.ResponseHeaders.Add(header.Key, header.Value);
|
||||
data.Log(new LogEntry(header.Key + ": " + header.Value, Colors.LightPink));
|
||||
}
|
||||
|
||||
// Get cookies
|
||||
data.Log(new LogEntry("Received cookies:", Colors.Goldenrod));
|
||||
data.Cookies = response.Cookies;
|
||||
foreach (var cookie in response.Cookies)
|
||||
{
|
||||
data.Log(new LogEntry(cookie.Key + ": " + cookie.Value, Colors.LightGoldenrodYellow));
|
||||
}
|
||||
|
||||
// Save the response content
|
||||
switch (responseType)
|
||||
{
|
||||
case ResponseType.String:
|
||||
data.Log(new LogEntry("Response Source:", Colors.Green));
|
||||
if (readResponseSource)
|
||||
{
|
||||
data.ResponseSource = response.ToString();
|
||||
data.Log(new LogEntry(data.ResponseSource, Colors.GreenYellow));
|
||||
}
|
||||
else
|
||||
{
|
||||
data.ResponseSource = "";
|
||||
data.Log(new LogEntry("[SKIPPED]", Colors.GreenYellow));
|
||||
}
|
||||
break;
|
||||
|
||||
case ResponseType.File:
|
||||
var file = ReplaceValues(downloadPath, data);
|
||||
using (var stream = File.Create(file)) { response.ToMemoryStream().CopyTo(stream); }
|
||||
data.Log(new LogEntry("File saved as " + file, Colors.Green));
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
data.Log(new LogEntry(ex.Message, Colors.White));
|
||||
if (ex.GetType() == typeof(HttpException))
|
||||
{
|
||||
data.ResponseCode = ((HttpException)ex).HttpStatusCode.ToString();
|
||||
data.Log(new LogEntry("Status code: " + data.ResponseCode, Colors.Cyan));
|
||||
}
|
||||
|
||||
if (!data.ConfigSettings.IgnoreResponseErrors) throw;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
#region Custom Cookies, Headers and Multipart Contents
|
||||
/// <summary>
|
||||
/// Builds a string containing custom cookies.
|
||||
/// </summary>
|
||||
/// <returns>One cookie per line, with name and value separated by a colon</returns>
|
||||
public string GetCustomCookies()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
foreach (var pair in CustomCookies)
|
||||
{
|
||||
sb.Append($"{pair.Key}: {pair.Value}");
|
||||
if (!pair.Equals(CustomCookies.Last())) sb.Append(Environment.NewLine);
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets custom cookies from an array of lines.
|
||||
/// </summary>
|
||||
/// <param name="lines">The lines containing the colon-separated name and value of the cookies</param>
|
||||
public void SetCustomCookies(string[] lines)
|
||||
{
|
||||
CustomCookies.Clear();
|
||||
foreach (var line in lines)
|
||||
{
|
||||
if (line.Contains(':'))
|
||||
{
|
||||
var split = line.Split(new[] { ':' }, 2);
|
||||
CustomCookies[split[0].Trim()] = split[1].Trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds a string containing custom headers.
|
||||
/// </summary>
|
||||
/// <returns>One header per line, with name and value separated by a colon</returns>
|
||||
public string GetCustomHeaders()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
foreach (var pair in CustomHeaders)
|
||||
{
|
||||
sb.Append($"{pair.Key}: {pair.Value}");
|
||||
if (!pair.Equals(CustomHeaders.Last())) sb.Append(Environment.NewLine);
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets custom headers from an array of lines.
|
||||
/// </summary>
|
||||
/// <param name="lines">The lines containing the colon-separated name and value of the headers</param>
|
||||
public void SetCustomHeaders(string[] lines)
|
||||
{
|
||||
CustomHeaders.Clear();
|
||||
foreach (var line in lines)
|
||||
{
|
||||
if (line.Contains(':'))
|
||||
{
|
||||
var split = line.Split(new[] { ':' }, 2);
|
||||
CustomHeaders[split[0].Trim()] = split[1].Trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds a string containing multipart content.
|
||||
/// </summary>
|
||||
/// <returns>One content per line, with type, name and value separated by a colon</returns>
|
||||
public string GetMultipartContents()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
foreach (var c in MultipartContents)
|
||||
{
|
||||
sb.Append($"{c.Type.ToString().ToUpper()}: {c.Name}: {c.Value}");
|
||||
if (!c.Equals(MultipartContents.Last())) sb.Append(Environment.NewLine);
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets multipart contents from an array of lines.
|
||||
/// </summary>
|
||||
/// <param name="lines">The lines containing the colon-separated type, name and value of the multipart contents</param>
|
||||
public void SetMultipartContents(string[] lines)
|
||||
{
|
||||
MultipartContents.Clear();
|
||||
foreach(var line in lines)
|
||||
{
|
||||
try
|
||||
{
|
||||
var split = line.Split(new[] { ':' }, 3);
|
||||
MultipartContents.Add(new MultipartContent() {
|
||||
Type = (MultipartContentType)Enum.Parse(typeof(MultipartContentType), split[0].Trim(), true),
|
||||
Name = split[1].Trim(),
|
||||
Value = split[2].Trim()
|
||||
});
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Generates a random string to be used for boundary.
|
||||
/// </summary>
|
||||
/// <returns>The random 16-character string</returns>
|
||||
internal static string GenerateMultipartBoundary()
|
||||
{
|
||||
StringBuilder builder = new StringBuilder();
|
||||
Random random = new Random();
|
||||
char ch;
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
ch = Convert.ToChar(Convert.ToInt32(Math.Floor(26 * random.NextDouble() + 65)));
|
||||
builder.Append(ch);
|
||||
}
|
||||
return builder.ToString().ToLower();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a Multipart Content
|
||||
/// </summary>
|
||||
public class MultipartContent
|
||||
{
|
||||
/// <summary>The type of multipart content.</summary>
|
||||
public MultipartContentType Type { get; set; } = MultipartContentType.String;
|
||||
|
||||
/// <summary>The name of the multipart content.</summary>
|
||||
public string Name { get; set; } = "";
|
||||
|
||||
/// <summary>The value of the multipart content (a string value or a file path).</summary>
|
||||
public string Value { get; set; } = "";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,254 @@
|
||||
using RuriLib.LS;
|
||||
using RuriLib.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Security;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace RuriLib
|
||||
{
|
||||
/// <summary>
|
||||
/// Available commands for the TCP client.
|
||||
/// </summary>
|
||||
public enum TCPCommand
|
||||
{
|
||||
/// <summary>Connects the client to a host.</summary>
|
||||
Connect,
|
||||
|
||||
/// <summary>Disconnects the client from the connected host.</summary>
|
||||
Disconnect,
|
||||
|
||||
/// <summary>Sends a message to the connected host.</summary>
|
||||
Send
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A block that can connect to a host over TCP and supports SSL.
|
||||
/// </summary>
|
||||
public class BlockTCP : BlockBase
|
||||
{
|
||||
private TCPCommand tcpCommand = TCPCommand.Connect;
|
||||
/// <summary>The command for the TCP client.</summary>
|
||||
public TCPCommand TCPCommand { get { return tcpCommand; } set { tcpCommand = value; OnPropertyChanged(); } }
|
||||
|
||||
private string host = "";
|
||||
/// <summary>The host to connect to.</summary>
|
||||
public string Host { get { return host; } set { host = value; OnPropertyChanged(); } }
|
||||
|
||||
private string port = "";
|
||||
/// <summary>The port to connect to.</summary>
|
||||
public string Port { get { return port; } set { port = value; OnPropertyChanged(); } }
|
||||
|
||||
private bool useSSL = true;
|
||||
/// <summary>Whether the client will communicate over the Secure Sockets Layer.</summary>
|
||||
public bool UseSSL { get { return useSSL; } set { useSSL = value; OnPropertyChanged(); } }
|
||||
|
||||
private string message = "";
|
||||
/// <summary>The message sent to the host.</summary>
|
||||
public string Message { get { return message; } set { message = value; OnPropertyChanged(); } }
|
||||
|
||||
private string variableName = "";
|
||||
/// <summary>The name of the output variable where the TCP response will be stored.</summary>
|
||||
public string VariableName { get { return variableName; } set { variableName = value; OnPropertyChanged(); } }
|
||||
|
||||
private bool isCapture = false;
|
||||
/// <summary>Whether the output variable should be marked for Capture.</summary>
|
||||
public bool IsCapture { get { return isCapture; } set { isCapture = value; OnPropertyChanged(); } }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a TCP block.
|
||||
/// </summary>
|
||||
public BlockTCP()
|
||||
{
|
||||
Label = "TCP";
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override BlockBase FromLS(string line)
|
||||
{
|
||||
// Trim the line
|
||||
var input = line.Trim();
|
||||
|
||||
// Parse the label
|
||||
if (input.StartsWith("#"))
|
||||
Label = LineParser.ParseLabel(ref input);
|
||||
|
||||
// Parse the function
|
||||
TCPCommand = (TCPCommand)LineParser.ParseEnum(ref input, "Command", typeof(TCPCommand));
|
||||
|
||||
// Parse specific function parameters
|
||||
switch (TCPCommand)
|
||||
{
|
||||
case TCPCommand.Connect:
|
||||
Host = LineParser.ParseLiteral(ref input, "Host");
|
||||
Port = LineParser.ParseLiteral(ref input, "Port");
|
||||
|
||||
if (LineParser.Lookahead(ref input) == TokenType.Boolean)
|
||||
LineParser.SetBool(ref input, this);
|
||||
|
||||
break;
|
||||
|
||||
case TCPCommand.Send:
|
||||
Message = LineParser.ParseLiteral(ref input, "Message");
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Try to parse the arrow, otherwise just return the block as is with default var name and var / cap choice
|
||||
if (LineParser.ParseToken(ref input, TokenType.Arrow, false) == "")
|
||||
return this;
|
||||
|
||||
// Parse the VAR / CAP
|
||||
try
|
||||
{
|
||||
var varType = LineParser.ParseToken(ref input, TokenType.Parameter, true);
|
||||
if (varType.ToUpper() == "VAR" || varType.ToUpper() == "CAP")
|
||||
IsCapture = varType.ToUpper() == "CAP";
|
||||
}
|
||||
catch { throw new ArgumentException("Invalid or missing variable type"); }
|
||||
|
||||
// Parse the variable/capture name
|
||||
try { VariableName = LineParser.ParseToken(ref input, TokenType.Literal, true); }
|
||||
catch { throw new ArgumentException("Variable name not specified"); }
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToLS(bool indent = true)
|
||||
{
|
||||
var writer = new BlockWriter(GetType(), indent, Disabled);
|
||||
writer
|
||||
.Label(Label)
|
||||
.Token("TCP")
|
||||
.Token(TCPCommand);
|
||||
|
||||
switch (TCPCommand)
|
||||
{
|
||||
case TCPCommand.Connect:
|
||||
writer
|
||||
.Literal(Host)
|
||||
.Literal(Port)
|
||||
.Boolean(UseSSL, "UseSSL");
|
||||
break;
|
||||
|
||||
case TCPCommand.Send:
|
||||
writer
|
||||
.Literal(Message);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!writer.CheckDefault(VariableName, "VariableName"))
|
||||
writer
|
||||
.Arrow()
|
||||
.Token(IsCapture ? "CAP" : "VAR")
|
||||
.Literal(VariableName);
|
||||
|
||||
return writer.ToString();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Process(BotData data)
|
||||
{
|
||||
// Get easy handles
|
||||
var tcp = data.TCPClient;
|
||||
var net = data.NETStream;
|
||||
var ssl = data.SSLStream;
|
||||
byte[] buffer = new byte[2048];
|
||||
int bytes = -1;
|
||||
string response = "";
|
||||
|
||||
switch (TCPCommand)
|
||||
{
|
||||
case TCPCommand.Connect:
|
||||
// Replace the Host and Port
|
||||
var h = ReplaceValues(host, data);
|
||||
var p = int.Parse(ReplaceValues(port, data));
|
||||
|
||||
// Initialize the TCP client, connect to the host and get the SSL stream
|
||||
tcp = new TcpClient();
|
||||
tcp.Connect(h, p);
|
||||
net = tcp.GetStream();
|
||||
if (UseSSL)
|
||||
{
|
||||
ssl = new SslStream(net);
|
||||
ssl.AuthenticateAsClient(h);
|
||||
}
|
||||
|
||||
// Read the stream to make sure we are connected
|
||||
if (UseSSL) bytes = ssl.Read(buffer, 0, buffer.Length);
|
||||
else bytes = net.Read(buffer, 0, buffer.Length);
|
||||
|
||||
// Save the response as ASCII in the SOURCE variable
|
||||
response = Encoding.ASCII.GetString(buffer, 0, bytes);
|
||||
|
||||
// Save the TCP client and the streams
|
||||
data.TCPClient = tcp;
|
||||
data.NETStream = net;
|
||||
data.SSLStream = ssl;
|
||||
data.TCPSSL = UseSSL;
|
||||
|
||||
data.Log(new LogEntry($"Succesfully connected to host {h} on port {p}. The server says:", Colors.Green));
|
||||
data.Log(new LogEntry(response, Colors.GreenYellow));
|
||||
|
||||
if (VariableName != "")
|
||||
{
|
||||
data.Variables.Set(new CVar(VariableName, response, IsCapture));
|
||||
data.Log(new LogEntry($"Saved Response in variable {VariableName}", Colors.White));
|
||||
}
|
||||
break;
|
||||
|
||||
case TCPCommand.Disconnect:
|
||||
if (tcp == null)
|
||||
{
|
||||
throw new Exception("Make a connection first!");
|
||||
}
|
||||
|
||||
tcp.Close();
|
||||
tcp = null;
|
||||
if (net != null) net.Close();
|
||||
if (ssl != null) ssl.Close();
|
||||
data.Log(new LogEntry($"Succesfully closed the stream", Colors.GreenYellow));
|
||||
break;
|
||||
|
||||
case TCPCommand.Send:
|
||||
if (tcp == null)
|
||||
{
|
||||
throw new Exception("Make a connection first!");
|
||||
}
|
||||
|
||||
var msg = ReplaceValues(Message, data);
|
||||
var b = Encoding.ASCII.GetBytes(msg.Replace(@"\r\n", "\r\n"));
|
||||
data.Log(new LogEntry("> " + msg, Colors.White));
|
||||
|
||||
if (data.TCPSSL)
|
||||
{
|
||||
ssl.Write(b);
|
||||
bytes = ssl.Read(buffer, 0, buffer.Length);
|
||||
}
|
||||
else
|
||||
{
|
||||
net.Write(b, 0, b.Length);
|
||||
bytes = net.Read(buffer, 0, buffer.Length);
|
||||
}
|
||||
|
||||
// Save the response as ASCII in the SOURCE variable and log it
|
||||
response = Encoding.ASCII.GetString(buffer, 0, bytes);
|
||||
data.Log(new LogEntry("> " + response, Colors.GreenYellow));
|
||||
|
||||
if (VariableName != "")
|
||||
{
|
||||
data.Variables.Set(new CVar(VariableName, response, IsCapture));
|
||||
data.Log(new LogEntry($"Saved Response in variable {VariableName}.", Colors.White));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,574 @@
|
||||
using RuriLib.LS;
|
||||
using RuriLib.Models;
|
||||
using RuriLib.ViewModels;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Text;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace RuriLib
|
||||
{
|
||||
/// <summary>
|
||||
/// The available utility groups.
|
||||
/// </summary>
|
||||
public enum UtilityGroup
|
||||
{
|
||||
/// <summary>The group of actions performed on list variables.</summary>
|
||||
List,
|
||||
|
||||
/// <summary>The group of actions performed on single variables.</summary>
|
||||
Variable,
|
||||
|
||||
/// <summary>The group of action to convert an encoded string.</summary>
|
||||
Conversion,
|
||||
|
||||
/// <summary>The group of actions that interact with files.</summary>
|
||||
File
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Actions executed on single variables.
|
||||
/// </summary>
|
||||
public enum VarAction
|
||||
{
|
||||
/// <summary>Splits a variable into a list given a separator.</summary>
|
||||
Split
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Actions executed on list variables.
|
||||
/// </summary>
|
||||
public enum ListAction
|
||||
{
|
||||
/// <summary>Creates an empty list variable.</summary>
|
||||
Create,
|
||||
|
||||
/// <summary>Joins a list into a single string, separating the elements with a separator.</summary>
|
||||
Join,
|
||||
|
||||
/// <summary>Concatenates two lists into a longer list variable.</summary>
|
||||
Concat,
|
||||
|
||||
/// <summary>Zips two lists into a new list variable where the elements are joined two by two.</summary>
|
||||
Zip,
|
||||
|
||||
/// <summary>Maps two lists into a dictionary variable.</summary>
|
||||
Map,
|
||||
|
||||
/// <summary>Adds an element to a list variable.</summary>
|
||||
Add,
|
||||
|
||||
/// <summary>Removes an element from a list variable.</summary>
|
||||
Remove,
|
||||
|
||||
/// <summary>Picks a random element from a list variable.</summary>
|
||||
Random
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The available conversion formats.
|
||||
/// </summary>
|
||||
public enum Conversion
|
||||
{
|
||||
/// <summary>A hexadecimal representation of a byte array.</summary>
|
||||
HEX,
|
||||
|
||||
/// <summary>A binary representation of a byte array, containing a multiple of 8 binary digits.</summary>
|
||||
BIN,
|
||||
|
||||
/// <summary>A base64 representation of a byte array.</summary>
|
||||
BASE64
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Actions that can be performed on files.
|
||||
/// </summary>
|
||||
public enum FileAction
|
||||
{
|
||||
/// <summary>Reads a file to a single variable.</summary>
|
||||
Read,
|
||||
|
||||
/// <summary>Reads a file to a list variable.</summary>
|
||||
ReadLines,
|
||||
|
||||
/// <summary>Writes a single variable to a file.</summary>
|
||||
Write,
|
||||
|
||||
/// <summary>Writes a list variable to a file.</summary>
|
||||
WriteLines,
|
||||
|
||||
/// <summary>Appends a single variable to a file.</summary>
|
||||
Append,
|
||||
|
||||
/// <summary>Appends a list variable to a file.</summary>
|
||||
AppendLines
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A block that performs actions on variables, converts values and operates on files.
|
||||
/// </summary>
|
||||
public class BlockUtility : BlockBase
|
||||
{
|
||||
// General
|
||||
private UtilityGroup group = UtilityGroup.List;
|
||||
/// <summary>The utility group.</summary>
|
||||
public UtilityGroup Group { get { return group; } set { group = value; OnPropertyChanged(); } }
|
||||
|
||||
private string variableName = "";
|
||||
/// <summary>The name of the output variable.</summary>
|
||||
public string VariableName { get { return variableName; } set { variableName = value; OnPropertyChanged(); } }
|
||||
|
||||
private bool isCapture = false;
|
||||
/// <summary>Whether the output variable should be marked for Capture.</summary>
|
||||
public bool IsCapture { get { return isCapture; } set { isCapture = value; OnPropertyChanged(); } }
|
||||
|
||||
private string inputString = "";
|
||||
/// <summary>The input to process.</summary>
|
||||
public string InputString { get { return inputString; } set { inputString = value; OnPropertyChanged(); } }
|
||||
|
||||
// Lists
|
||||
private ListAction listAction = ListAction.Join;
|
||||
/// <summary>The action to be performed on a list variable.</summary>
|
||||
public ListAction ListAction { get { return listAction; } set { listAction = value; OnPropertyChanged(); } }
|
||||
|
||||
private string listName = "";
|
||||
/// <summary>The name of the target list variable.</summary>
|
||||
public string ListName { get { return listName; } set { listName = value; OnPropertyChanged(); } }
|
||||
|
||||
#region ListUtility Specific Variables
|
||||
private string separator = ","; // Join
|
||||
/// <summary>The separator for joining a list.</summary>
|
||||
public string Separator { get { return separator; } set { separator = value; OnPropertyChanged(); } }
|
||||
|
||||
private string secondListName = ""; // Zip
|
||||
/// <summary>The name of the second list variable.</summary>
|
||||
public string SecondListName { get { return secondListName; } set { secondListName = value; OnPropertyChanged(); } }
|
||||
|
||||
private string listItem = ""; // Add
|
||||
/// <summary>The list item to add.</summary>
|
||||
public string ListItem { get { return listItem; } set { listItem = value; OnPropertyChanged(); } }
|
||||
|
||||
private string listIndex = "-1"; // Add (-1 = end, 0 = start)
|
||||
/// <summary>The list index where an item can be added/removed. -1 = end, 0 = start.</summary>
|
||||
public string ListIndex { get { return listIndex; } set { listIndex = value; OnPropertyChanged(); } }
|
||||
#endregion
|
||||
|
||||
// Variables
|
||||
private VarAction varAction = VarAction.Split;
|
||||
/// <summary>The action to be performed on a single variable.</summary>
|
||||
public VarAction VarAction { get { return varAction; } set { varAction = value; OnPropertyChanged(); } }
|
||||
|
||||
private string varName = "";
|
||||
/// <summary>The name of the single variable.</summary>
|
||||
public string VarName { get { return varName; } set { varName = value; OnPropertyChanged(); } }
|
||||
|
||||
#region Variables Specific
|
||||
private string splitSeparator = "";
|
||||
/// <summary>The separator to split a string into a list.</summary>
|
||||
public string SplitSeparator { get { return splitSeparator; } set { splitSeparator = value; OnPropertyChanged(); } }
|
||||
#endregion
|
||||
|
||||
// Conversion
|
||||
private Conversion conversionFrom = Conversion.HEX;
|
||||
/// <summary>The encoding to convert from.</summary>
|
||||
public Conversion ConversionFrom { get { return conversionFrom; } set { conversionFrom = value; OnPropertyChanged(); } }
|
||||
|
||||
private Conversion conversionTo = Conversion.BASE64;
|
||||
/// <summary>The encoding to convert to.</summary>
|
||||
public Conversion ConversionTo { get { return conversionTo; } set { conversionTo = value; OnPropertyChanged(); } }
|
||||
|
||||
// Files
|
||||
private string filePath = "test.txt";
|
||||
/// <summary>The path to the file to read/write.</summary>
|
||||
public string FilePath { get { return filePath; } set { filePath = value; OnPropertyChanged(); } }
|
||||
|
||||
private FileAction fileAction = FileAction.Read;
|
||||
/// <summary>The action to be performed on the file.</summary>
|
||||
public FileAction FileAction { get { return fileAction; } set { fileAction = value; OnPropertyChanged(); } }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a Utility block.
|
||||
/// </summary>
|
||||
public BlockUtility()
|
||||
{
|
||||
Label = "UTILITY";
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override BlockBase FromLS(string line)
|
||||
{
|
||||
// Trim the line
|
||||
var input = line.Trim();
|
||||
|
||||
// Parse the label
|
||||
if (input.StartsWith("#"))
|
||||
Label = LineParser.ParseLabel(ref input);
|
||||
|
||||
// Parse the function
|
||||
Group = (UtilityGroup)LineParser.ParseEnum(ref input, "Group", typeof(UtilityGroup));
|
||||
|
||||
// Parse specific function parameters
|
||||
switch (Group)
|
||||
{
|
||||
case UtilityGroup.List:
|
||||
|
||||
ListName = LineParser.ParseLiteral(ref input, "List Name");
|
||||
ListAction = (ListAction)LineParser.ParseEnum(ref input, "List Action", typeof(ListAction));
|
||||
|
||||
switch (ListAction)
|
||||
{
|
||||
case ListAction.Join:
|
||||
Separator = LineParser.ParseLiteral(ref input, "Separator");
|
||||
break;
|
||||
|
||||
case ListAction.Concat:
|
||||
case ListAction.Zip:
|
||||
case ListAction.Map:
|
||||
SecondListName = LineParser.ParseLiteral(ref input, "Second List Name");
|
||||
break;
|
||||
|
||||
case ListAction.Add:
|
||||
ListItem = LineParser.ParseLiteral(ref input, "Item");
|
||||
ListIndex = LineParser.ParseLiteral(ref input, "Index");
|
||||
break;
|
||||
|
||||
case ListAction.Remove:
|
||||
ListIndex = LineParser.ParseLiteral(ref input, "Index");
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case UtilityGroup.Variable:
|
||||
VarName = LineParser.ParseLiteral(ref input, "Var Name");
|
||||
VarAction = (VarAction)LineParser.ParseEnum(ref input, "Var Action", typeof(VarAction));
|
||||
|
||||
switch (VarAction)
|
||||
{
|
||||
case VarAction.Split:
|
||||
SplitSeparator = LineParser.ParseLiteral(ref input, "Separator");
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case UtilityGroup.Conversion:
|
||||
ConversionFrom = (Conversion)LineParser.ParseEnum(ref input, "Conversion From", typeof(Conversion));
|
||||
ConversionTo = (Conversion)LineParser.ParseEnum(ref input, "Conversion To", typeof(Conversion));
|
||||
InputString = LineParser.ParseLiteral(ref input, "Input");
|
||||
break;
|
||||
|
||||
case UtilityGroup.File:
|
||||
FilePath = LineParser.ParseLiteral(ref input, "File Name");
|
||||
FileAction = (FileAction)LineParser.ParseEnum(ref input, "File Action", typeof(FileAction));
|
||||
|
||||
switch (FileAction)
|
||||
{
|
||||
case FileAction.Write:
|
||||
case FileAction.WriteLines:
|
||||
case FileAction.Append:
|
||||
case FileAction.AppendLines:
|
||||
InputString = LineParser.ParseLiteral(ref input, "Input String");
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Try to parse the arrow, otherwise just return the block as is with default var name and var / cap choice
|
||||
if (LineParser.ParseToken(ref input, TokenType.Arrow, false) == "")
|
||||
return this;
|
||||
|
||||
// Parse the VAR / CAP
|
||||
try
|
||||
{
|
||||
var varType = LineParser.ParseToken(ref input, TokenType.Parameter, true);
|
||||
if (varType.ToUpper() == "VAR" || varType.ToUpper() == "CAP")
|
||||
IsCapture = varType.ToUpper() == "CAP";
|
||||
}
|
||||
catch { throw new ArgumentException("Invalid or missing variable type"); }
|
||||
|
||||
// Parse the variable/capture name
|
||||
try { VariableName = LineParser.ParseToken(ref input, TokenType.Literal, true); }
|
||||
catch { throw new ArgumentException("Variable name not specified"); }
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToLS(bool indent = true)
|
||||
{
|
||||
var writer = new BlockWriter(GetType(), indent, Disabled);
|
||||
writer
|
||||
.Label(Label)
|
||||
.Token("UTILITY")
|
||||
.Token(Group);
|
||||
|
||||
switch (Group)
|
||||
{
|
||||
case UtilityGroup.List:
|
||||
writer
|
||||
.Literal(ListName)
|
||||
.Token(ListAction);
|
||||
|
||||
switch (ListAction)
|
||||
{
|
||||
case ListAction.Join:
|
||||
writer
|
||||
.Literal(Separator);
|
||||
break;
|
||||
|
||||
case ListAction.Concat:
|
||||
case ListAction.Zip:
|
||||
case ListAction.Map:
|
||||
writer
|
||||
.Literal(SecondListName);
|
||||
break;
|
||||
|
||||
case ListAction.Add:
|
||||
writer
|
||||
.Literal(ListItem)
|
||||
.Literal(ListIndex);
|
||||
break;
|
||||
|
||||
case ListAction.Remove:
|
||||
writer
|
||||
.Literal(ListIndex);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case UtilityGroup.Variable:
|
||||
writer
|
||||
.Literal(VariableName)
|
||||
.Token(VarAction);
|
||||
|
||||
switch (VarAction)
|
||||
{
|
||||
case VarAction.Split:
|
||||
writer
|
||||
.Literal(SplitSeparator);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case UtilityGroup.Conversion:
|
||||
writer
|
||||
.Token(ConversionFrom)
|
||||
.Token(ConversionTo)
|
||||
.Literal(InputString);
|
||||
break;
|
||||
|
||||
case UtilityGroup.File:
|
||||
writer
|
||||
.Literal(FilePath)
|
||||
.Token(FileAction);
|
||||
|
||||
switch (FileAction)
|
||||
{
|
||||
case FileAction.Write:
|
||||
case FileAction.WriteLines:
|
||||
case FileAction.Append:
|
||||
case FileAction.AppendLines:
|
||||
writer
|
||||
.Literal(InputString);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (!writer.CheckDefault(VariableName, "VariableName"))
|
||||
writer
|
||||
.Arrow()
|
||||
.Token(IsCapture ? "CAP" : "VAR")
|
||||
.Literal(VariableName);
|
||||
|
||||
return writer.ToString();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Process(BotData data)
|
||||
{
|
||||
base.Process(data);
|
||||
|
||||
try
|
||||
{
|
||||
switch (group)
|
||||
{
|
||||
case UtilityGroup.List:
|
||||
|
||||
var list = data.Variables.GetList(listName);
|
||||
var list2 = data.Variables.GetList(secondListName);
|
||||
var item = ReplaceValues(listItem, data);
|
||||
var index = int.Parse(ReplaceValues(listIndex, data));
|
||||
CVar variable = null;
|
||||
|
||||
switch (listAction)
|
||||
{
|
||||
case ListAction.Create:
|
||||
data.Variables.Set(new CVar(variableName, new List<string>()));
|
||||
break;
|
||||
|
||||
case ListAction.Join:
|
||||
data.Variables.Set(new CVar(variableName, string.Join(separator, list), isCapture));
|
||||
break;
|
||||
|
||||
case ListAction.Concat:
|
||||
data.Variables.Set(new CVar(variableName, list.Concat(list2).ToList(), isCapture));
|
||||
break;
|
||||
|
||||
case ListAction.Zip:
|
||||
data.Variables.Set(new CVar(variableName, list.Zip(list2, (a, b) => a + b).ToList(), isCapture));
|
||||
break;
|
||||
|
||||
case ListAction.Map:
|
||||
data.Variables.Set(new CVar(variableName, list.Zip(list2, (k, v) => new { k, v }).ToDictionary(x => x.k, x => x.v), isCapture));
|
||||
break;
|
||||
|
||||
case ListAction.Add:
|
||||
variable = data.Variables.Get(listName, CVar.VarType.List);
|
||||
if (variable == null) variable = data.GlobalVariables.Get(listName, CVar.VarType.List);
|
||||
if (variable == null) break;
|
||||
if (variable.Value.Count == 0) index = 0;
|
||||
else if (index < 0) index += variable.Value.Count;
|
||||
variable.Value.Insert(index, item);
|
||||
break;
|
||||
|
||||
case ListAction.Remove:
|
||||
variable = data.Variables.Get(listName, CVar.VarType.List);
|
||||
if (variable == null) variable = data.GlobalVariables.Get(listName, CVar.VarType.List);
|
||||
if (variable == null) break;
|
||||
if (variable.Value.Count == 0) index = 0;
|
||||
else if (index < 0) index += variable.Value.Count;
|
||||
variable.Value.RemoveAt(index);
|
||||
break;
|
||||
|
||||
case ListAction.Random:
|
||||
data.Variables.Set(new CVar(variableName, list[data.rand.Next(list.Count)], isCapture));
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
data.Log(new LogEntry(string.Format("Executed action {0} on list {1}", listAction, listName), Colors.White));
|
||||
break;
|
||||
|
||||
case UtilityGroup.Variable:
|
||||
string single;
|
||||
switch (varAction)
|
||||
{
|
||||
case VarAction.Split:
|
||||
single = data.Variables.GetSingle(varName);
|
||||
data.Variables.Set(new CVar(variableName, single.Split(new string[] { ReplaceValues(separator, data) }, StringSplitOptions.None).ToList()));
|
||||
break;
|
||||
}
|
||||
data.Log(new LogEntry(string.Format("Executed action {0} on variable {1}", varAction, varName), Colors.White));
|
||||
break;
|
||||
|
||||
case UtilityGroup.Conversion:
|
||||
byte[] convertedBytes = ConvertFrom(ReplaceValues(inputString, data), conversionFrom);
|
||||
data.Variables.Set(new CVar(variableName, ConvertTo(convertedBytes, conversionTo), isCapture));
|
||||
data.Log(new LogEntry(string.Format("Converted input from {0} to {1}", conversionFrom, conversionTo), Colors.White));
|
||||
break;
|
||||
|
||||
case UtilityGroup.File:
|
||||
var file = ReplaceValues(filePath, data);
|
||||
var input = ReplaceValues(inputString, data).Replace("\\r\\n", "\r\n").Replace("\\n", "\n");
|
||||
var inputs = ReplaceValuesRecursive(inputString, data);
|
||||
switch (fileAction)
|
||||
{
|
||||
case FileAction.Read:
|
||||
data.Variables.Set(new CVar(variableName, File.ReadAllText(file), isCapture));
|
||||
break;
|
||||
|
||||
case FileAction.ReadLines:
|
||||
data.Variables.Set(new CVar(variableName, File.ReadAllLines(file).ToList(), isCapture));
|
||||
break;
|
||||
|
||||
case FileAction.Write:
|
||||
File.WriteAllText(file, input);
|
||||
break;
|
||||
|
||||
case FileAction.WriteLines:
|
||||
File.WriteAllLines(file, inputs);
|
||||
break;
|
||||
|
||||
case FileAction.Append:
|
||||
File.AppendAllText(file, input);
|
||||
break;
|
||||
|
||||
case FileAction.AppendLines:
|
||||
File.AppendAllLines(file, inputs);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch(Exception ex) { data.Log(new LogEntry(ex.Message, Colors.Tomato)); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts an encoded input to a byte array.
|
||||
/// </summary>
|
||||
/// <param name="input">The encoded input</param>
|
||||
/// <param name="type">The encoding</param>
|
||||
/// <returns>The converted byte array</returns>
|
||||
public byte[] ConvertFrom(string input, Conversion type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case Conversion.BASE64:
|
||||
return Convert.FromBase64String(input);
|
||||
|
||||
case Conversion.HEX:
|
||||
input = new string(input.ToCharArray()
|
||||
.Where(c => !char.IsWhiteSpace(c))
|
||||
.ToArray()).Replace("0x", "");
|
||||
return Enumerable.Range(0, input.Length)
|
||||
.Where(x => x % 2 == 0)
|
||||
.Select(x => Convert.ToByte(input.Substring(x, 2), 16))
|
||||
.ToArray();
|
||||
|
||||
case Conversion.BIN:
|
||||
int numOfBytes = input.Length / 8;
|
||||
byte[] bytes = new byte[numOfBytes];
|
||||
for (int i = 0; i < numOfBytes; ++i) { bytes[i] = Convert.ToByte(input.Substring(8 * i, 8), 2); }
|
||||
return bytes;
|
||||
|
||||
default:
|
||||
return new byte[0];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a byte array to an encoded string.
|
||||
/// </summary>
|
||||
/// <param name="input">The byte array to encode</param>
|
||||
/// <param name="type">The encoding</param>
|
||||
/// <returns>The encoded string</returns>
|
||||
public string ConvertTo(byte[] input, Conversion type)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
switch (type)
|
||||
{
|
||||
case Conversion.BASE64:
|
||||
return Convert.ToBase64String(input);
|
||||
|
||||
case Conversion.HEX:
|
||||
foreach (byte b in input)
|
||||
sb.AppendFormat("{0:x2}", b);
|
||||
return sb.ToString().ToUpper();
|
||||
|
||||
case Conversion.BIN:
|
||||
return string.Concat(input.Select(b => Convert.ToString(b, 2).PadLeft(8, '0')));
|
||||
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,396 @@
|
||||
using OpenQA.Selenium;
|
||||
using OpenQA.Selenium.Chrome;
|
||||
using OpenQA.Selenium.Firefox;
|
||||
using OpenQA.Selenium.Interactions;
|
||||
using RuriLib.LS;
|
||||
using RuriLib.ViewModels;
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace RuriLib
|
||||
{
|
||||
/// <summary>
|
||||
/// The actions that can be performed on a browser.
|
||||
/// </summary>
|
||||
public enum BrowserAction
|
||||
{
|
||||
/// <summary>Starts the driver and opens the browser.</summary>
|
||||
Open,
|
||||
|
||||
/// <summary>Closes the browser but not the driver.</summary>
|
||||
Close,
|
||||
|
||||
/// <summary>Closes the browser and disposes the driver.</summary>
|
||||
Quit,
|
||||
|
||||
/// <summary>Clears the cookies in the browser.</summary>
|
||||
ClearCookies,
|
||||
|
||||
/// <summary>Sends some keystrokes to the browser. Special keys like <ENTER> can be sent, separated by two pipe characters e.g. <TAB>||<ENTER></summary>
|
||||
SendKeys,
|
||||
|
||||
/// <summary>Takes a screenshot of the visible part of the page.</summary>
|
||||
Screenshot,
|
||||
|
||||
/// <summary>Scrolls to the top of the page.</summary>
|
||||
ScrollToTop,
|
||||
|
||||
/// <summary>Scrolls to the bottom of the page.</summary>
|
||||
ScrollToBottom,
|
||||
|
||||
/// <summary>Scrolls down by a given number of pixels from the top of the page.</summary>
|
||||
Scroll,
|
||||
|
||||
/// <summary>Switches to the browser tab with a given index.</summary>
|
||||
SwitchToTab,
|
||||
|
||||
/// <summary>Refreshes the current page.</summary>
|
||||
Refresh,
|
||||
|
||||
/// <summary>Goes to the previous page.</summary>
|
||||
Back,
|
||||
|
||||
/// <summary>Goes to the next page.</summary>
|
||||
Forward,
|
||||
|
||||
/// <summary>Maximizes the browser window.</summary>
|
||||
Maximize,
|
||||
|
||||
/// <summary>Minimizes the browser window.</summary>
|
||||
Minimize,
|
||||
|
||||
/// <summary>Sets the browser window as full screen.</summary>
|
||||
FullScreen,
|
||||
|
||||
/// <summary>Sets the browser window's width.</summary>
|
||||
SetWidth,
|
||||
|
||||
/// <summary>Sets the browser window's height.</summary>
|
||||
SetHeight,
|
||||
|
||||
/// <summary>Sets the DOM (the page source modified by the javascript code) into the SOURCE variable.</summary>
|
||||
DOMtoSOURCE,
|
||||
|
||||
/// <summary>Transfers the cookies from the browser to the HTTP cookie jar.</summary>
|
||||
GetCookies,
|
||||
|
||||
/// <summary>Transfers the cookies from the HTTP cookie jar to the browser.</summary>
|
||||
SetCookies,
|
||||
|
||||
/// <summary>Switches to the default content.</summary>
|
||||
SwitchToDefault,
|
||||
|
||||
/// <summary>Switches to the alert message.</summary>
|
||||
SwitchToAlert,
|
||||
|
||||
/// <summary>Switches to the default frame (useful to get out of an iframe).</summary>
|
||||
SwitchToParentFrame
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A block that can interact with a selenium-driven browser.
|
||||
/// </summary>
|
||||
public class SBlockBrowserAction : BlockBase
|
||||
{
|
||||
private BrowserAction action = BrowserAction.Open;
|
||||
/// <summary>The action that is performed on the browser.</summary>
|
||||
public BrowserAction Action { get { return action; } set { action = value; OnPropertyChanged(); } }
|
||||
|
||||
private string input = "";
|
||||
/// <summary>The input string.</summary>
|
||||
public string Input { get { return input; } set { input = value; OnPropertyChanged(); } }
|
||||
|
||||
// Constructor
|
||||
/// <summary>
|
||||
/// Creates a BrowserAction block.
|
||||
/// </summary>
|
||||
public SBlockBrowserAction()
|
||||
{
|
||||
Label = "BROWSER ACTION";
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override BlockBase FromLS(string line)
|
||||
{
|
||||
// Trim the line
|
||||
var input = line.Trim();
|
||||
|
||||
// Parse the label
|
||||
if (input.StartsWith("#"))
|
||||
Label = LineParser.ParseLabel(ref input);
|
||||
|
||||
/*
|
||||
* Syntax:
|
||||
* BROWSERACTION ACTION ["INPUT"]
|
||||
* */
|
||||
|
||||
Action = (BrowserAction)LineParser.ParseEnum(ref input, "ACTION", typeof(BrowserAction));
|
||||
|
||||
if (input != "") Input = LineParser.ParseLiteral(ref input, "INPUT");
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToLS(bool indent = true)
|
||||
{
|
||||
var writer = new BlockWriter(GetType(), indent, Disabled);
|
||||
writer
|
||||
.Label(Label)
|
||||
.Token("BROWSERACTION")
|
||||
.Token(Action)
|
||||
.Literal(Input, "Input");
|
||||
return writer.ToString();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Process(BotData data)
|
||||
{
|
||||
base.Process(data);
|
||||
|
||||
if (data.Driver == null && action != BrowserAction.Open)
|
||||
{
|
||||
data.Log(new LogEntry("Open a browser first!", Colors.White));
|
||||
throw new Exception("Browser not open");
|
||||
}
|
||||
|
||||
var replacedInput = ReplaceValues(input, data);
|
||||
|
||||
switch (action)
|
||||
{
|
||||
case BrowserAction.Open:
|
||||
OpenBrowser(data);
|
||||
try { UpdateSeleniumData(data); } catch { }
|
||||
break;
|
||||
|
||||
case BrowserAction.Close:
|
||||
data.Driver.Close();
|
||||
data.BrowserOpen = false;
|
||||
break;
|
||||
|
||||
case BrowserAction.Quit:
|
||||
data.Driver.Quit();
|
||||
data.BrowserOpen = false;
|
||||
break;
|
||||
|
||||
case BrowserAction.ClearCookies:
|
||||
data.Driver.Manage().Cookies.DeleteAllCookies();
|
||||
break;
|
||||
|
||||
case BrowserAction.SendKeys:
|
||||
var action = new Actions(data.Driver);
|
||||
foreach(var s in replacedInput.Split(new string[] { "||" }, StringSplitOptions.None))
|
||||
{
|
||||
switch (s)
|
||||
{
|
||||
case "<TAB>":
|
||||
action.SendKeys(OpenQA.Selenium.Keys.Tab);
|
||||
break;
|
||||
|
||||
case "<ENTER>":
|
||||
action.SendKeys(OpenQA.Selenium.Keys.Enter);
|
||||
break;
|
||||
|
||||
case "<BACKSPACE>":
|
||||
action.SendKeys(OpenQA.Selenium.Keys.Backspace);
|
||||
break;
|
||||
|
||||
case "<ESC>":
|
||||
action.SendKeys(OpenQA.Selenium.Keys.Escape);
|
||||
break;
|
||||
|
||||
default:
|
||||
action.SendKeys(s);
|
||||
break;
|
||||
}
|
||||
}
|
||||
action.Perform();
|
||||
Thread.Sleep(1000);
|
||||
if(replacedInput.Contains("<ENTER>") || replacedInput.Contains("<BACKSPACE>")) // These might lead to a page change
|
||||
UpdateSeleniumData(data);
|
||||
break;
|
||||
|
||||
case BrowserAction.Screenshot:
|
||||
var image = data.Driver.GetScreenshot();
|
||||
SaveScreenshot(image, data);
|
||||
break;
|
||||
|
||||
case BrowserAction.SwitchToTab:
|
||||
data.Driver.SwitchTo().Window(data.Driver.WindowHandles[int.Parse(replacedInput)]);
|
||||
UpdateSeleniumData(data);
|
||||
break;
|
||||
|
||||
case BrowserAction.Refresh:
|
||||
data.Driver.Navigate().Refresh();
|
||||
break;
|
||||
|
||||
case BrowserAction.Back:
|
||||
data.Driver.Navigate().Back();
|
||||
break;
|
||||
|
||||
case BrowserAction.Forward:
|
||||
data.Driver.Navigate().Forward();
|
||||
break;
|
||||
|
||||
case BrowserAction.Maximize:
|
||||
data.Driver.Manage().Window.Maximize();
|
||||
break;
|
||||
|
||||
case BrowserAction.Minimize:
|
||||
data.Driver.Manage().Window.Minimize();
|
||||
break;
|
||||
|
||||
case BrowserAction.FullScreen:
|
||||
data.Driver.Manage().Window.FullScreen();
|
||||
break;
|
||||
|
||||
case BrowserAction.SetWidth:
|
||||
data.Driver.Manage().Window.Size = new Size(int.Parse(replacedInput), data.Driver.Manage().Window.Size.Height);
|
||||
break;
|
||||
|
||||
case BrowserAction.SetHeight:
|
||||
data.Driver.Manage().Window.Size = new Size(data.Driver.Manage().Window.Size.Width, int.Parse(replacedInput));
|
||||
break;
|
||||
|
||||
case BrowserAction.DOMtoSOURCE:
|
||||
data.ResponseSource = data.Driver.FindElement(By.TagName("body")).GetAttribute("innerHTML");
|
||||
break;
|
||||
|
||||
case BrowserAction.GetCookies:
|
||||
foreach(var cookie in data.Driver.Manage().Cookies.AllCookies)
|
||||
{
|
||||
try { data.Cookies.Add(cookie.Name, cookie.Value); } catch { }
|
||||
}
|
||||
break;
|
||||
|
||||
case BrowserAction.SetCookies:
|
||||
var baseURL = Regex.Match(ReplaceValues(input, data), "^(?:https?:\\/\\/)?(?:[^@\\/\n]+@)?(?:www\\.)?([^:\\/?\n]+)").Groups[1].Value;
|
||||
foreach (var cookie in data.Cookies)
|
||||
{
|
||||
try { data.Driver.Manage().Cookies.AddCookie(new Cookie(cookie.Key, cookie.Value, baseURL, "/", DateTime.MaxValue)); } catch { }
|
||||
}
|
||||
break;
|
||||
|
||||
case BrowserAction.SwitchToDefault:
|
||||
data.Driver.SwitchTo().DefaultContent();
|
||||
break;
|
||||
|
||||
case BrowserAction.SwitchToAlert:
|
||||
data.Driver.SwitchTo().Alert();
|
||||
break;
|
||||
|
||||
case BrowserAction.SwitchToParentFrame:
|
||||
data.Driver.SwitchTo().ParentFrame();
|
||||
break;
|
||||
}
|
||||
|
||||
data.Log(new LogEntry(string.Format("Executed browser action {0} on input {1}", action, ReplaceValues(input, data)), Colors.White));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens a browser of the given type (if not already open) and with the given settings.
|
||||
/// </summary>
|
||||
/// <param name="data">The BotData where the settings are stored.</param>
|
||||
public static void OpenBrowser(BotData data)
|
||||
{
|
||||
if (!data.BrowserOpen)
|
||||
{
|
||||
data.Log(new LogEntry("Opening browser...", Colors.White));
|
||||
|
||||
switch (data.GlobalSettings.Selenium.Browser)
|
||||
{
|
||||
case BrowserType.Chrome:
|
||||
try
|
||||
{
|
||||
ChromeOptions chromeop = new ChromeOptions();
|
||||
ChromeDriverService chromeservice = ChromeDriverService.CreateDefaultService();
|
||||
chromeservice.SuppressInitialDiagnosticInformation = true;
|
||||
chromeservice.HideCommandPromptWindow = true;
|
||||
chromeservice.EnableVerboseLogging = false;
|
||||
chromeop.AddArgument("--log-level=3");
|
||||
chromeop.BinaryLocation = data.GlobalSettings.Selenium.ChromeBinaryLocation;
|
||||
if (data.GlobalSettings.Selenium.Headless || data.ConfigSettings.ForceHeadless) chromeop.AddArgument("--headless");
|
||||
else if (data.GlobalSettings.Selenium.ChromeExtensions.Count > 0) // This should only be done when not headless
|
||||
chromeop.AddExtensions(data.GlobalSettings.Selenium.ChromeExtensions
|
||||
.Where(ext => ext.EndsWith(".crx"))
|
||||
.Select(ext => Directory.GetCurrentDirectory() + "\\ChromeExtensions\\" + ext));
|
||||
if (data.ConfigSettings.DisableNotifications) chromeop.AddArgument("--disable-notifications");
|
||||
if (data.ConfigSettings.CustomCMDArgs != "") chromeop.AddArgument(data.ConfigSettings.CustomCMDArgs);
|
||||
if (data.ConfigSettings.RandomUA) chromeop.AddArgument("--user-agent=" + BlockFunction.RandomUserAgent(data.rand));
|
||||
else if (data.ConfigSettings.CustomUserAgent != "") chromeop.AddArgument("--user-agent=" + data.ConfigSettings.CustomUserAgent);
|
||||
|
||||
if (data.UseProxies) chromeop.AddArgument("--proxy-server=" + data.Proxy.Type.ToString().ToLower() + "://" + data.Proxy.Proxy);
|
||||
|
||||
data.Driver = new ChromeDriver(chromeservice, chromeop);
|
||||
}
|
||||
catch (Exception ex) { data.Log(new LogEntry(ex.ToString(), Colors.White)); return; }
|
||||
|
||||
break;
|
||||
|
||||
case BrowserType.Firefox:
|
||||
try
|
||||
{
|
||||
FirefoxOptions fireop = new FirefoxOptions();
|
||||
FirefoxDriverService fireservice = FirefoxDriverService.CreateDefaultService();
|
||||
FirefoxProfile fireprofile = new FirefoxProfile();
|
||||
|
||||
fireservice.SuppressInitialDiagnosticInformation = true;
|
||||
fireservice.HideCommandPromptWindow = true;
|
||||
fireop.AddArgument("--log-level=3");
|
||||
fireop.BrowserExecutableLocation = data.GlobalSettings.Selenium.FirefoxBinaryLocation;
|
||||
if (data.GlobalSettings.Selenium.Headless || data.ConfigSettings.ForceHeadless) fireop.AddArgument("--headless");
|
||||
if (data.ConfigSettings.DisableNotifications) fireprofile.SetPreference("dom.webnotifications.enabled", false);
|
||||
if (data.ConfigSettings.CustomCMDArgs != "") fireop.AddArgument(data.ConfigSettings.CustomCMDArgs);
|
||||
if (data.ConfigSettings.RandomUA) fireprofile.SetPreference("general.useragent.override", BlockFunction.RandomUserAgent(data.rand));
|
||||
else if (data.ConfigSettings.CustomUserAgent != "") fireprofile.SetPreference("general.useragent.override", data.ConfigSettings.CustomUserAgent);
|
||||
|
||||
if (data.UseProxies)
|
||||
{
|
||||
fireprofile.SetPreference("network.proxy.type", 1);
|
||||
if (data.Proxy.Type == Extreme.Net.ProxyType.Http)
|
||||
{
|
||||
fireprofile.SetPreference("network.proxy.http", data.Proxy.Host);
|
||||
fireprofile.SetPreference("network.proxy.httpport", int.Parse(data.Proxy.Port));
|
||||
fireprofile.SetPreference("network.proxy.ssl", data.Proxy.Host);
|
||||
fireprofile.SetPreference("network.proxy.sslport", int.Parse(data.Proxy.Port));
|
||||
}
|
||||
else
|
||||
{
|
||||
fireprofile.SetPreference("network.proxy.socks", data.Proxy.Host);
|
||||
fireprofile.SetPreference("network.proxy.socksport", int.Parse(data.Proxy.Port));
|
||||
if (data.Proxy.Type == Extreme.Net.ProxyType.Socks4)
|
||||
fireprofile.SetPreference("network.proxy.socksversion", 4);
|
||||
else if (data.Proxy.Type == Extreme.Net.ProxyType.Socks5)
|
||||
fireprofile.SetPreference("network.proxy.socksversion", 5);
|
||||
}
|
||||
}
|
||||
|
||||
fireop.Profile = fireprofile;
|
||||
data.Driver = new FirefoxDriver(fireservice, fireop, new TimeSpan(0, 1, 0));
|
||||
|
||||
}
|
||||
catch(Exception ex) { data.Log(new LogEntry(ex.ToString(), Colors.White)); return; }
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
data.Driver.Manage().Timeouts().PageLoad = TimeSpan.FromSeconds(data.GlobalSettings.Selenium.PageLoadTimeout);
|
||||
data.Log(new LogEntry("Opened!", Colors.White));
|
||||
data.BrowserOpen = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
UpdateSeleniumData(data);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,388 @@
|
||||
using OpenQA.Selenium;
|
||||
using RuriLib.LS;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace RuriLib
|
||||
{
|
||||
/// <summary>
|
||||
/// The available ways to address an element.
|
||||
/// </summary>
|
||||
public enum ElementLocator
|
||||
{
|
||||
/// <summary>The id of the element.</summary>
|
||||
Id,
|
||||
|
||||
/// <summary>The class of the element.</summary>
|
||||
Class,
|
||||
|
||||
/// <summary>The name of the element.</summary>
|
||||
Name,
|
||||
|
||||
/// <summary>The tag of the element.</summary>
|
||||
Tag,
|
||||
|
||||
/// <summary>The CSS selector of the element.</summary>
|
||||
Selector,
|
||||
|
||||
/// <summary>The xpath of the element.</summary>
|
||||
XPath
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The actions that can be performed on an element.
|
||||
/// </summary>
|
||||
public enum ElementAction
|
||||
{
|
||||
/// <summary>Clears the text of an input element.</summary>
|
||||
Clear,
|
||||
|
||||
/// <summary>Sends keystrokes to an input element.</summary>
|
||||
SendKeys,
|
||||
|
||||
/// <summary>Types keystrokes into an input element with random delays between each keystroke, like a human would.</summary>
|
||||
SendKeysHuman,
|
||||
|
||||
/// <summary>Clicks an element.</summary>
|
||||
Click,
|
||||
|
||||
/// <summary>Submits a form element.</summary>
|
||||
Submit,
|
||||
|
||||
/// <summary>Gets the text inside an element.</summary>
|
||||
GetText,
|
||||
|
||||
/// <summary>Gets a given attribute of an element.</summary>
|
||||
GetAttribute,
|
||||
|
||||
/// <summary>Checks if the element is currently displayed on the page.</summary>
|
||||
IsDisplayed,
|
||||
|
||||
/// <summary>Checks if the element is enabled on the page.</summary>
|
||||
IsEnabled,
|
||||
|
||||
/// <summary>Checks if the element is selected.</summary>
|
||||
IsSelected,
|
||||
|
||||
/// <summary>Retrieves the X coordinate of the top-left corner of the element.</summary>
|
||||
LocationX,
|
||||
|
||||
/// <summary>Retrieves the Y coordinate of the top-left corner of the element.</summary>
|
||||
LocationY,
|
||||
|
||||
/// <summary>Retrieves the width of the element.</summary>
|
||||
SizeX,
|
||||
|
||||
/// <summary>Retrieves the height of the element.</summary>
|
||||
SizeY,
|
||||
|
||||
/// <summary>Takes a screenshot of the element.</summary>
|
||||
Screenshot,
|
||||
|
||||
/// <summary>Switches to the iframe element.</summary>
|
||||
SwitchToFrame,
|
||||
|
||||
/// <summary>Waits until the element appears in the DOM (up to a specified timeout).</summary>
|
||||
WaitForElement
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A block that can perform an action on an element inside an HTML page.
|
||||
/// </summary>
|
||||
public class SBlockElementAction : BlockBase
|
||||
{
|
||||
private ElementLocator locator = ElementLocator.Id;
|
||||
/// <summary>The element locator.</summary>
|
||||
public ElementLocator Locator { get { return locator; } set { locator = value; OnPropertyChanged(); } }
|
||||
|
||||
private string elementString = "";
|
||||
/// <summary>The value of the locator.</summary>
|
||||
public string ElementString { get { return elementString; } set { elementString = value; OnPropertyChanged(); } }
|
||||
|
||||
private int elementIndex = 0;
|
||||
/// <summary>The index of the element in case the locator matches more than one.</summary>
|
||||
public int ElementIndex { get { return elementIndex; } set { elementIndex = value; OnPropertyChanged(); } }
|
||||
|
||||
private ElementAction action = ElementAction.Clear;
|
||||
/// <summary>The action to be performed on the element.</summary>
|
||||
public ElementAction Action { get { return action; } set { action = value; OnPropertyChanged(); } }
|
||||
|
||||
private string input = "";
|
||||
/// <summary>The input data.</summary>
|
||||
public string Input { get { return input; } set { input = value; OnPropertyChanged(); } }
|
||||
|
||||
private string outputVariable = "";
|
||||
/// <summary>The name of the output variable.</summary>
|
||||
public string OutputVariable { get { return outputVariable; } set { outputVariable = value; OnPropertyChanged(); } }
|
||||
|
||||
private bool isCapture = false;
|
||||
/// <summary>Whether the output variable should be marked for Capture.</summary>
|
||||
public bool IsCapture { get { return isCapture; } set { isCapture = value; OnPropertyChanged(); } }
|
||||
|
||||
private bool recursive = false;
|
||||
/// <summary>Whether the GetText and GetAttribute actions should be executed on all the elements matched by the locator and return a list of values.</summary>
|
||||
public bool Recursive { get { return recursive; } set { recursive = value; OnPropertyChanged(); } }
|
||||
|
||||
/// <summary>
|
||||
/// Creates an element action block.
|
||||
/// </summary>
|
||||
public SBlockElementAction()
|
||||
{
|
||||
Label = "ELEMENT ACTION";
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override BlockBase FromLS(string line)
|
||||
{
|
||||
// Trim the line
|
||||
var input = line.Trim();
|
||||
|
||||
// Parse the label
|
||||
if (input.StartsWith("#"))
|
||||
Label = LineParser.ParseLabel(ref input);
|
||||
|
||||
/*
|
||||
* Syntax:
|
||||
* ELEMENTACTION ID "id" 0 RECURSIVE? ACTION ["INPUT"] [-> VAR/CAP "OUTPUT"]
|
||||
* */
|
||||
|
||||
Locator = (ElementLocator)LineParser.ParseEnum(ref input, "LOCATOR", typeof(ElementLocator));
|
||||
ElementString = LineParser.ParseLiteral(ref input, "STRING");
|
||||
if (LineParser.Lookahead(ref input) == TokenType.Boolean)
|
||||
LineParser.SetBool(ref input, this);
|
||||
else if (LineParser.Lookahead(ref input) == TokenType.Integer)
|
||||
ElementIndex = LineParser.ParseInt(ref input, "INDEX");
|
||||
Action = (ElementAction)LineParser.ParseEnum(ref input, "ACTION", typeof(ElementAction));
|
||||
|
||||
if (input == "") return this;
|
||||
if (LineParser.Lookahead(ref input) == TokenType.Literal)
|
||||
Input = LineParser.ParseLiteral(ref input, "INPUT");
|
||||
|
||||
// Try to parse the arrow, otherwise just return the block as is with default var name and var / cap choice
|
||||
if (LineParser.ParseToken(ref input, TokenType.Arrow, false) == "")
|
||||
return this;
|
||||
|
||||
// Parse the VAR / CAP
|
||||
try
|
||||
{
|
||||
var varType = LineParser.ParseToken(ref input, TokenType.Parameter, true);
|
||||
if (varType.ToUpper() == "VAR" || varType.ToUpper() == "CAP")
|
||||
IsCapture = varType.ToUpper() == "CAP";
|
||||
}
|
||||
catch { throw new ArgumentException("Invalid or missing variable type"); }
|
||||
|
||||
// Parse the variable/capture name
|
||||
try { OutputVariable = LineParser.ParseToken(ref input, TokenType.Literal, true); }
|
||||
catch { throw new ArgumentException("Variable name not specified"); }
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToLS(bool indent = true)
|
||||
{
|
||||
var writer = new BlockWriter(GetType(), indent, Disabled);
|
||||
writer
|
||||
.Label(Label)
|
||||
.Token("ELEMENTACTION")
|
||||
.Token(Locator)
|
||||
.Literal(ElementString);
|
||||
|
||||
if (Recursive) writer.Boolean(Recursive, "Recursive");
|
||||
else if (ElementIndex != 0) writer.Integer(ElementIndex, "ElementIndex");
|
||||
|
||||
writer
|
||||
.Indent()
|
||||
.Token(Action)
|
||||
.Literal(Input, "Input");
|
||||
|
||||
if (!writer.CheckDefault(OutputVariable, "OutputVariable"))
|
||||
{
|
||||
writer
|
||||
.Arrow()
|
||||
.Token(IsCapture ? "CAP" : "VAR")
|
||||
.Literal(OutputVariable);
|
||||
}
|
||||
|
||||
return writer.ToString();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Process(BotData data)
|
||||
{
|
||||
base.Process(data);
|
||||
|
||||
if (data.Driver == null)
|
||||
{
|
||||
data.Log(new LogEntry("Open a browser first!", Colors.White));
|
||||
throw new Exception("Browser not open");
|
||||
}
|
||||
|
||||
// Find the element
|
||||
IWebElement element = null;
|
||||
ReadOnlyCollection<IWebElement> elements = null;
|
||||
try
|
||||
{
|
||||
if(action != ElementAction.WaitForElement)
|
||||
{
|
||||
elements = FindElements(data);
|
||||
element = elements[elementIndex];
|
||||
}
|
||||
}
|
||||
catch { data.Log(new LogEntry("Cannot find element on the page", Colors.White)); }
|
||||
|
||||
List<string> outputs = new List<string>();
|
||||
try
|
||||
{
|
||||
switch (action)
|
||||
{
|
||||
case ElementAction.Clear:
|
||||
element.Clear();
|
||||
break;
|
||||
|
||||
case ElementAction.SendKeys:
|
||||
element.SendKeys(ReplaceValues(input, data));
|
||||
break;
|
||||
|
||||
case ElementAction.Click:
|
||||
element.Click();
|
||||
UpdateSeleniumData(data);
|
||||
break;
|
||||
|
||||
case ElementAction.Submit:
|
||||
element.Submit();
|
||||
UpdateSeleniumData(data);
|
||||
break;
|
||||
|
||||
case ElementAction.GetText:
|
||||
if (recursive) foreach (var elem in elements) outputs.Add(elem.Text);
|
||||
else outputs.Add(element.Text);
|
||||
break;
|
||||
|
||||
case ElementAction.GetAttribute:
|
||||
if (recursive) foreach (var elem in elements) outputs.Add(elem.GetAttribute(ReplaceValues(input, data)));
|
||||
else outputs.Add(element.GetAttribute(ReplaceValues(input, data)));
|
||||
break;
|
||||
|
||||
case ElementAction.IsDisplayed:
|
||||
outputs.Add(element.Displayed.ToString());
|
||||
break;
|
||||
|
||||
case ElementAction.IsEnabled:
|
||||
outputs.Add(element.Enabled.ToString());
|
||||
break;
|
||||
|
||||
case ElementAction.IsSelected:
|
||||
outputs.Add(element.Selected.ToString());
|
||||
break;
|
||||
|
||||
case ElementAction.LocationX:
|
||||
outputs.Add(element.Location.X.ToString());
|
||||
break;
|
||||
|
||||
case ElementAction.LocationY:
|
||||
outputs.Add(element.Location.Y.ToString());
|
||||
break;
|
||||
|
||||
case ElementAction.SizeX:
|
||||
outputs.Add(element.Size.Width.ToString());
|
||||
break;
|
||||
|
||||
case ElementAction.SizeY:
|
||||
outputs.Add(element.Size.Height.ToString());
|
||||
break;
|
||||
|
||||
case ElementAction.Screenshot:
|
||||
var image = GetElementScreenShot(data.Driver, element);
|
||||
SaveScreenshot(image, data);
|
||||
break;
|
||||
|
||||
case ElementAction.SwitchToFrame:
|
||||
data.Driver.SwitchTo().Frame(element);
|
||||
break;
|
||||
|
||||
case ElementAction.WaitForElement:
|
||||
var ms = 0; // Currently waited milliseconds
|
||||
var max = 10000;
|
||||
try { max = int.Parse(input) * 1000; } catch { }// Max ms to wait
|
||||
var found = false;
|
||||
while(ms < max)
|
||||
{
|
||||
try
|
||||
{
|
||||
elements = FindElements(data);
|
||||
element = elements[0];
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
catch { ms += 200; Thread.Sleep(200); }
|
||||
}
|
||||
if (!found) { data.Log(new LogEntry("Timeout while waiting for element", Colors.White)); }
|
||||
break;
|
||||
|
||||
case ElementAction.SendKeysHuman:
|
||||
var toSend = ReplaceValues(input, data);
|
||||
var rand = new Random();
|
||||
foreach(char c in toSend) {
|
||||
element.SendKeys(c.ToString());
|
||||
Thread.Sleep(rand.Next(100, 300));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch { data.Log(new LogEntry("Cannot execute action on the element", Colors.White)); }
|
||||
|
||||
data.Log(new LogEntry(string.Format("Executed action {0} on the element with input {1}", action, ReplaceValues(input, data)), Colors.White));
|
||||
|
||||
if (outputs.Count != 0)
|
||||
{
|
||||
InsertVariables(data, isCapture, recursive, outputs, outputVariable, "", "");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Screenshots an element on the page.
|
||||
/// </summary>
|
||||
/// <param name="driver">The selenium driver</param>
|
||||
/// <param name="element">The element to screenshot</param>
|
||||
/// <returns>The bitmap screenshot of the element</returns>
|
||||
public static Bitmap GetElementScreenShot(IWebDriver driver, IWebElement element)
|
||||
{
|
||||
Screenshot sc = ((ITakesScreenshot)driver).GetScreenshot();
|
||||
var img = Image.FromStream(new MemoryStream(sc.AsByteArray)) as Bitmap;
|
||||
return img.Clone(new Rectangle(element.Location, element.Size), img.PixelFormat);
|
||||
}
|
||||
|
||||
private ReadOnlyCollection<IWebElement> FindElements(BotData data)
|
||||
{
|
||||
switch (locator)
|
||||
{
|
||||
case ElementLocator.Id:
|
||||
return data.Driver.FindElements(By.Id(ReplaceValues(elementString, data)));
|
||||
|
||||
case ElementLocator.Class:
|
||||
return data.Driver.FindElements(By.ClassName(ReplaceValues(elementString, data)));
|
||||
|
||||
case ElementLocator.Name:
|
||||
return data.Driver.FindElements(By.Name(ReplaceValues(elementString, data)));
|
||||
|
||||
case ElementLocator.Tag:
|
||||
return data.Driver.FindElements(By.TagName(ReplaceValues(elementString, data)));
|
||||
|
||||
case ElementLocator.Selector:
|
||||
return data.Driver.FindElements(By.CssSelector(ReplaceValues(elementString, data)));
|
||||
|
||||
case ElementLocator.XPath:
|
||||
return data.Driver.FindElements(By.XPath(ReplaceValues(elementString, data)));
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
using RuriLib.LS;
|
||||
using System;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace RuriLib
|
||||
{
|
||||
/// <summary>
|
||||
/// A block that executes javascript code in the selenium-driven browser.
|
||||
/// </summary>
|
||||
public class SBlockExecuteJS : BlockBase
|
||||
{
|
||||
private string javascriptCode = "alert('henlo');";
|
||||
/// <summary>The javascript code.</summary>
|
||||
public string JavascriptCode { get { return javascriptCode; } set { javascriptCode = value; OnPropertyChanged(); } }
|
||||
|
||||
/// <summary>
|
||||
/// Creates an ExecuteJS block.
|
||||
/// </summary>
|
||||
public SBlockExecuteJS()
|
||||
{
|
||||
Label = "EXECUTE JS";
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override BlockBase FromLS(string line)
|
||||
{
|
||||
// Trim the line
|
||||
var input = line.Trim();
|
||||
|
||||
// Parse the label
|
||||
if (input.StartsWith("#"))
|
||||
Label = LineParser.ParseLabel(ref input);
|
||||
|
||||
/*
|
||||
* Syntax:
|
||||
* EXECUTEJS "SCRIPT"
|
||||
* */
|
||||
|
||||
JavascriptCode = LineParser.ParseLiteral(ref input, "SCRIPT");
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToLS(bool indent = true)
|
||||
{
|
||||
var writer = new BlockWriter(GetType(), indent, Disabled);
|
||||
writer
|
||||
.Label(Label)
|
||||
.Token("EXECUTEJS")
|
||||
.Literal(JavascriptCode.Replace("\r\n", " ").Replace("\n", " "));
|
||||
return writer.ToString();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Process(BotData data)
|
||||
{
|
||||
base.Process(data);
|
||||
|
||||
if (data.Driver == null)
|
||||
{
|
||||
data.Log(new LogEntry("Open a browser first!", Colors.White));
|
||||
throw new Exception("Browser not open");
|
||||
}
|
||||
|
||||
data.Log(new LogEntry("Executing JS code!", Colors.White));
|
||||
data.Driver.ExecuteScript(ReplaceValues(javascriptCode, data));
|
||||
data.Log(new LogEntry("... executed!", Colors.White));
|
||||
|
||||
UpdateSeleniumData(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
using RuriLib.LS;
|
||||
using System;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace RuriLib
|
||||
{
|
||||
/// <summary>
|
||||
/// A block that navigates to a given URL in a selenium-driven browser.
|
||||
/// </summary>
|
||||
public class SBlockNavigate : BlockBase
|
||||
{
|
||||
private string url = "https://example.com";
|
||||
/// <summary>The destination URL.</summary>
|
||||
public string Url { get { return url; } set { url = value; OnPropertyChanged(); } }
|
||||
|
||||
private int timeout = 60;
|
||||
/// <summary>The maximum time to wait for the page to load.</summary>
|
||||
public int Timeout { get { return timeout; } set { timeout = value; OnPropertyChanged(); } }
|
||||
|
||||
private bool banOnTimeout = true;
|
||||
/// <summary>Whether to set the status to BAN after a timeout.</summary>
|
||||
public bool BanOnTimeout { get { return banOnTimeout; } set { banOnTimeout = value; OnPropertyChanged(); } }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a Navigate block.
|
||||
/// </summary>
|
||||
public SBlockNavigate()
|
||||
{
|
||||
Label = "NAVIGATE";
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override BlockBase FromLS(string line)
|
||||
{
|
||||
// Trim the line
|
||||
var input = line.Trim();
|
||||
|
||||
// Parse the label
|
||||
if (input.StartsWith("#"))
|
||||
Label = LineParser.ParseLabel(ref input);
|
||||
|
||||
/*
|
||||
* Syntax:
|
||||
* NAVIGATE "URL" TIMEOUT
|
||||
* */
|
||||
|
||||
Url = LineParser.ParseLiteral(ref input, "URL");
|
||||
if (LineParser.Lookahead(ref input) == TokenType.Integer)
|
||||
Timeout = LineParser.ParseInt(ref input, "TIMEOUT");
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToLS(bool indent = true)
|
||||
{
|
||||
var writer = new BlockWriter(GetType(), indent, Disabled);
|
||||
writer
|
||||
.Label(Label)
|
||||
.Token("NAVIGATE")
|
||||
.Literal(Url)
|
||||
.Integer(Timeout, "Timeout");
|
||||
return writer.ToString();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Process(BotData data)
|
||||
{
|
||||
base.Process(data);
|
||||
|
||||
if(data.Driver == null)
|
||||
{
|
||||
data.Log(new LogEntry("Open a browser first!", Colors.White));
|
||||
throw new Exception("Browser not open");
|
||||
}
|
||||
|
||||
data.Log(new LogEntry("Navigating to "+ReplaceValues(url,data), Colors.White));
|
||||
data.Driver.Manage().Timeouts().PageLoad = TimeSpan.FromSeconds(timeout);
|
||||
try
|
||||
{
|
||||
data.Driver.Navigate().GoToUrl(ReplaceValues(url, data));
|
||||
//data.Driver.Url = ReplaceValues(url, data);
|
||||
data.Log(new LogEntry("Navigated!", Colors.White));
|
||||
}
|
||||
catch (OpenQA.Selenium.WebDriverTimeoutException)
|
||||
{
|
||||
data.Log(new LogEntry("Timeout on Page Load", Colors.Tomato));
|
||||
if (BanOnTimeout)
|
||||
data.Status = BotStatus.BAN;
|
||||
}
|
||||
|
||||
data.Driver.Manage().Timeouts().PageLoad = TimeSpan.FromSeconds(data.GlobalSettings.Selenium.PageLoadTimeout);
|
||||
UpdateSeleniumData(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.Globalization;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
|
||||
namespace RuriLib.CaptchaServices
|
||||
{
|
||||
/// <summary>
|
||||
/// Implements the API of https://azcaptcha.com/
|
||||
/// </summary>
|
||||
public class AZCaptcha : CaptchaService
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates an AZCaptcha client.
|
||||
/// </summary>
|
||||
/// <param name="apiKey">The AZCaptcha API key</param>
|
||||
/// <param name="timeout">The maximum time to wait for the challenge to be solved</param>
|
||||
public AZCaptcha(string apiKey, int timeout) : base(apiKey, timeout) { }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override double GetBalance()
|
||||
{
|
||||
using (CWebClient client = new CWebClient())
|
||||
{
|
||||
if (Timeout > 0) client.Timeout = Timeout;
|
||||
var response = client.DownloadString($"http://azcaptcha.com/res.php?key={ApiKey}&action=getbalance&json=1");
|
||||
GenericResponse gbr = JsonConvert.DeserializeObject<GenericResponse>(response);
|
||||
if (gbr.status == 0) throw new Exception(gbr.request);
|
||||
return double.Parse(gbr.request, CultureInfo.InvariantCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string SolveRecaptcha(string siteKey, string siteUrl)
|
||||
{
|
||||
// Create task
|
||||
using (CWebClient client = new CWebClient())
|
||||
{
|
||||
if (Timeout > 0) client.Timeout = Timeout;
|
||||
var response = client.DownloadString($"http://azcaptcha.com/in.php?key={ApiKey}&method=userrecaptcha&googlekey={siteKey}&pageurl={siteUrl}&json=1");
|
||||
GenericResponse ctr = JsonConvert.DeserializeObject<GenericResponse>(response);
|
||||
if (ctr.status == 0) throw new Exception(ctr.request);
|
||||
TaskId = ctr.request;
|
||||
Status = CaptchaStatus.Processing;
|
||||
|
||||
// Check if task has been completed
|
||||
DateTime start = DateTime.Now;
|
||||
while (Status == CaptchaStatus.Processing && (DateTime.Now - start).TotalSeconds < Timeout)
|
||||
{
|
||||
Thread.Sleep(5000);
|
||||
response = client.DownloadString($"http://azcaptcha.com/res.php?key={ApiKey}&action=get&id={TaskId}&json=1");
|
||||
GenericResponse gtrr = JsonConvert.DeserializeObject<GenericResponse>(response);
|
||||
if (gtrr.request.Contains("NOTREADY")) continue;
|
||||
if (gtrr.status == 0) throw new Exception(gtrr.request);
|
||||
Status = CaptchaStatus.Completed;
|
||||
return gtrr.request;
|
||||
}
|
||||
throw new TimeoutException();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string SolveCaptcha(Bitmap bitmap)
|
||||
{
|
||||
// Create task
|
||||
using (CWebClient client = new CWebClient())
|
||||
{
|
||||
if (Timeout > 0) client.Timeout = Timeout;
|
||||
client.Headers.Add(HttpRequestHeader.ContentType, "application/x-www-form-urlencoded");
|
||||
var response = client.UploadString("http://azcaptcha.com/in.php",
|
||||
$"key={ApiKey}&method=base64®sense=1&body={GetBase64(bitmap, ImageFormat.Bmp)}&json=1");
|
||||
GenericResponse ctr = JsonConvert.DeserializeObject<GenericResponse>(response);
|
||||
if (ctr.status == 0) throw new Exception(ctr.request);
|
||||
TaskId = ctr.request;
|
||||
Status = CaptchaStatus.Processing;
|
||||
|
||||
// Check if task has been completed
|
||||
DateTime start = DateTime.Now;
|
||||
while (Status == CaptchaStatus.Processing && (DateTime.Now - start).TotalSeconds < Timeout)
|
||||
{
|
||||
Thread.Sleep(5000);
|
||||
response = client.DownloadString($"http://azcaptcha.com/res.php?key={ApiKey}&action=get&id={TaskId}&json=1");
|
||||
GenericResponse gtrr = JsonConvert.DeserializeObject<GenericResponse>(response);
|
||||
if (gtrr.request.Contains("NOTREADY")) continue;
|
||||
if (gtrr.status == 0) throw new Exception(gtrr.request);
|
||||
Status = CaptchaStatus.Completed;
|
||||
return gtrr.request;
|
||||
}
|
||||
throw new TimeoutException();
|
||||
}
|
||||
}
|
||||
|
||||
#pragma warning disable 0414
|
||||
#pragma warning disable 0649
|
||||
class GenericResponse
|
||||
{
|
||||
public int status;
|
||||
public string request;
|
||||
}
|
||||
#pragma warning restore 0414
|
||||
#pragma warning restore 0649
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
|
||||
namespace RuriLib.CaptchaServices
|
||||
{
|
||||
/// <summary>
|
||||
/// Implements the API of https://anti-captcha.com/
|
||||
/// </summary>
|
||||
public class AntiCaptcha : CaptchaService
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates an AntiCaptcha client.
|
||||
/// </summary>
|
||||
/// <param name="apiKey">The AntiCaptcha API key</param>
|
||||
/// <param name="timeout">The maximum time to wait for the challenge to be solved</param>
|
||||
public AntiCaptcha(string apiKey, int timeout) : base(apiKey, timeout) { }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override double GetBalance()
|
||||
{
|
||||
using (CWebClient client = new CWebClient())
|
||||
{
|
||||
if (Timeout > 0) client.Timeout = Timeout;
|
||||
client.Headers.Add(HttpRequestHeader.ContentType, "application/json");
|
||||
var response = client.UploadString("https://api.anti-captcha.com/getBalance",
|
||||
JsonConvert.SerializeObject(new GetBalanceRequest(ApiKey)));
|
||||
GetBalanceResponse gbr = JsonConvert.DeserializeObject<GetBalanceResponse>(response);
|
||||
if (gbr.errorId > 0) throw new Exception(gbr.errorCode);
|
||||
return gbr.balance;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string SolveRecaptcha(string siteKey, string siteUrl)
|
||||
{
|
||||
// Create task
|
||||
using (CWebClient client = new CWebClient())
|
||||
{
|
||||
if (Timeout > 0) client.Timeout = Timeout;
|
||||
client.Headers.Add(HttpRequestHeader.ContentType, "application/json");
|
||||
var response = client.UploadString("https://api.anti-captcha.com/createTask",
|
||||
JsonConvert.SerializeObject(new CreateRecaptchaTaskRequest(ApiKey, siteUrl, siteKey)));
|
||||
CreateTaskResponse ctr = JsonConvert.DeserializeObject<CreateTaskResponse>(response);
|
||||
if (ctr.errorId > 0) throw new Exception(ctr.errorCode);
|
||||
TaskId = ctr.taskId;
|
||||
Status = CaptchaStatus.Processing;
|
||||
|
||||
// Check if task has been completed
|
||||
DateTime start = DateTime.Now;
|
||||
while (Status == CaptchaStatus.Processing && (DateTime.Now - start).TotalSeconds < Timeout)
|
||||
{
|
||||
Thread.Sleep(5000);
|
||||
client.Headers.Add(HttpRequestHeader.ContentType, "application/json");
|
||||
response = client.UploadString("https://api.anti-captcha.com/getTaskResult",
|
||||
JsonConvert.SerializeObject(new GetTaskResultRequest(ApiKey, TaskId)));
|
||||
GetTaskResultRecaptchaResponse gtrr = JsonConvert.DeserializeObject<GetTaskResultRecaptchaResponse>(response);
|
||||
if (gtrr.errorId > 0) throw new Exception(gtrr.errorCode);
|
||||
if (gtrr.status == "ready")
|
||||
{
|
||||
Status = CaptchaStatus.Completed;
|
||||
return gtrr.solution.gRecaptchaResponse;
|
||||
}
|
||||
}
|
||||
throw new TimeoutException();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string SolveCaptcha(Bitmap bitmap)
|
||||
{
|
||||
// Create task
|
||||
using (CWebClient client = new CWebClient())
|
||||
{
|
||||
if (Timeout > 0) client.Timeout = Timeout;
|
||||
client.Headers.Add(HttpRequestHeader.ContentType, "application/json");
|
||||
var response = client.UploadString("https://api.anti-captcha.com/createTask",
|
||||
JsonConvert.SerializeObject(new CreateCaptchaTaskRequest(ApiKey, GetBase64(bitmap, ImageFormat.Png))));
|
||||
CreateTaskResponse ctr = JsonConvert.DeserializeObject<CreateTaskResponse>(response);
|
||||
if (ctr.errorId > 0) throw new Exception(ctr.errorCode);
|
||||
TaskId = ctr.taskId;
|
||||
Status = CaptchaStatus.Processing;
|
||||
|
||||
// Check if task has been completed
|
||||
DateTime start = DateTime.Now;
|
||||
while (Status == CaptchaStatus.Processing && (DateTime.Now - start).TotalSeconds < Timeout)
|
||||
{
|
||||
Thread.Sleep(5000);
|
||||
client.Headers.Add(HttpRequestHeader.ContentType, "application/json");
|
||||
response = client.UploadString("https://api.anti-captcha.com/getTaskResult",
|
||||
JsonConvert.SerializeObject(new GetTaskResultRequest(ApiKey, TaskId)));
|
||||
GetTaskResultCaptchaResponse gtrr = JsonConvert.DeserializeObject<GetTaskResultCaptchaResponse>(response);
|
||||
if (gtrr.errorId > 0) throw new Exception(gtrr.errorCode);
|
||||
if (gtrr.status == "ready")
|
||||
{
|
||||
Status = CaptchaStatus.Completed;
|
||||
return gtrr.solution.text;
|
||||
}
|
||||
}
|
||||
throw new TimeoutException();
|
||||
}
|
||||
}
|
||||
|
||||
#pragma warning disable 0414
|
||||
#pragma warning disable 0649
|
||||
class GetBalanceRequest
|
||||
{
|
||||
public string clientKey;
|
||||
public GetBalanceRequest(string apiKey) { clientKey = apiKey; }
|
||||
}
|
||||
|
||||
class GetBalanceResponse
|
||||
{
|
||||
public int errorId;
|
||||
public string errorCode;
|
||||
public double balance;
|
||||
}
|
||||
|
||||
class CreateRecaptchaTaskRequest
|
||||
{
|
||||
public string clientKey;
|
||||
public RecaptchaTask task;
|
||||
public CreateRecaptchaTaskRequest(string apiKey, string url, string key) { clientKey = apiKey; task = new RecaptchaTask(url, key); }
|
||||
}
|
||||
|
||||
class RecaptchaTask
|
||||
{
|
||||
public string type = "NoCaptchaTaskProxyless";
|
||||
public string websiteURL;
|
||||
public string websiteKey;
|
||||
public RecaptchaTask(string url, string key) { websiteURL = url; websiteKey = key; }
|
||||
}
|
||||
|
||||
class CreateCaptchaTaskRequest
|
||||
{
|
||||
public string clientKey;
|
||||
public CaptchaTask task;
|
||||
public CreateCaptchaTaskRequest(string apiKey, string base64) { clientKey = apiKey; task = new CaptchaTask(base64); }
|
||||
}
|
||||
|
||||
class CaptchaTask
|
||||
{
|
||||
public string type = "ImageToTextTask";
|
||||
public string body;
|
||||
public CaptchaTask(string base64) { body = base64; }
|
||||
}
|
||||
|
||||
class CreateTaskResponse
|
||||
{
|
||||
public int errorId;
|
||||
public int taskId;
|
||||
public string errorCode;
|
||||
}
|
||||
|
||||
class GetTaskResultRequest
|
||||
{
|
||||
public string clientKey;
|
||||
public int taskId;
|
||||
public GetTaskResultRequest(string apiKey, int id) { clientKey = apiKey; taskId = id; }
|
||||
}
|
||||
|
||||
class GetTaskResultCaptchaResponse
|
||||
{
|
||||
public int errorId;
|
||||
public string errorCode;
|
||||
public string status;
|
||||
public CaptchaSolution solution;
|
||||
}
|
||||
|
||||
class GetTaskResultRecaptchaResponse
|
||||
{
|
||||
public int errorId;
|
||||
public string errorCode;
|
||||
public string status;
|
||||
public RecaptchaSolution solution;
|
||||
}
|
||||
|
||||
class RecaptchaSolution
|
||||
{
|
||||
public string gRecaptchaResponse;
|
||||
}
|
||||
|
||||
class CaptchaSolution
|
||||
{
|
||||
public string text;
|
||||
}
|
||||
#pragma warning restore 0414
|
||||
#pragma warning restore 0649
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace RuriLib.CaptchaServices
|
||||
{
|
||||
/// <summary>
|
||||
/// A WebClient with customizable timeout.
|
||||
/// </summary>
|
||||
public class CWebClient : WebClient
|
||||
{
|
||||
/// <summary>The maximum time to wait for a response.</summary>
|
||||
public int Timeout { get; set; } = 100;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the WebRequest.
|
||||
/// </summary>
|
||||
/// <param name="uri">The uri</param>
|
||||
/// <returns>The WebRequest</returns>
|
||||
protected override WebRequest GetWebRequest(Uri uri)
|
||||
{
|
||||
WebRequest w = base.GetWebRequest(uri);
|
||||
w.Timeout = Timeout * 1000;
|
||||
return w;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace RuriLib.CaptchaServices
|
||||
{
|
||||
/// <summary>
|
||||
/// Represent a captcha-solving service.
|
||||
/// </summary>
|
||||
public abstract class CaptchaService
|
||||
{
|
||||
/// <summary>
|
||||
/// The busy status of the service.
|
||||
/// </summary>
|
||||
public enum CaptchaStatus
|
||||
{
|
||||
/// <summary>No captcha is being solved.</summary>
|
||||
Idle,
|
||||
|
||||
/// <summary>A captcha is being solved.</summary>
|
||||
Processing,
|
||||
|
||||
/// <summary>A captcha was solved.</summary>
|
||||
Completed
|
||||
}
|
||||
|
||||
/// <summary>The API key to access the captcha service.</summary>
|
||||
public string ApiKey { get; set; }
|
||||
|
||||
/// <summary>The username to access the captcha service.</summary>
|
||||
public string User { get; set; }
|
||||
|
||||
/// <summary>The password to access the captcha service.</summary>
|
||||
public string Pass { get; set; }
|
||||
|
||||
/// <summary>The maximum time to wait for captcha completion.</summary>
|
||||
public int Timeout { get; set; }
|
||||
|
||||
/// <summary>The id of the captcha-solving task being performed on the remote server, used to periodically query the status of the task.</summary>
|
||||
public dynamic TaskId { get; set; }
|
||||
|
||||
/// <summary>The status of the captcha service.</summary>
|
||||
public CaptchaStatus Status { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a captcha service that authenticates via API key.
|
||||
/// </summary>
|
||||
/// <param name="apiKey">The API key of the account</param>
|
||||
/// <param name="timeout">The maximum time to wait for captcha completion</param>
|
||||
public CaptchaService(string apiKey, int timeout)
|
||||
{
|
||||
ApiKey = apiKey;
|
||||
Timeout = timeout;
|
||||
Status = CaptchaStatus.Idle;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a captcha service that authenticates via username and password.
|
||||
/// </summary>
|
||||
/// <param name="user">The username of the account</param>
|
||||
/// <param name="pass">The password of the account</param>
|
||||
/// <param name="timeout">The maximum time to wait for captcha completion</param>
|
||||
public CaptchaService(string user, string pass, int timeout)
|
||||
{
|
||||
User = user;
|
||||
Pass = pass;
|
||||
Timeout = timeout;
|
||||
Status = CaptchaStatus.Idle;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the remaining balance in the account.
|
||||
/// </summary>
|
||||
/// <returns>The balance of the account</returns>
|
||||
public virtual double GetBalance()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Solves a reCaptcha challenge for a given site.
|
||||
/// </summary>
|
||||
/// <param name="siteKey">The SiteKey found in the page's source code</param>
|
||||
/// <param name="siteUrl">The URL of the site to solve a reCaptcha for</param>
|
||||
/// <returns>The g-recaptcha-response of the challenge</returns>
|
||||
public virtual string SolveRecaptcha(string siteKey, string siteUrl)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Solves an image captcha challenge.
|
||||
/// </summary>
|
||||
/// <param name="image">The bitmap image of the challenge</param>
|
||||
/// <returns>The text written in the image</returns>
|
||||
public virtual string SolveCaptcha(Bitmap image)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the base64-encoded string from an image.
|
||||
/// </summary>
|
||||
/// <param name="image">The bitmap image</param>
|
||||
/// <param name="format">The format of the bitmap image</param>
|
||||
/// <returns>The base64-encoded string</returns>
|
||||
protected string GetBase64(Bitmap image, ImageFormat format)
|
||||
{
|
||||
MemoryStream stream = new MemoryStream();
|
||||
image.Save(stream, format);
|
||||
byte[] imageBytes = stream.ToArray();
|
||||
return Convert.ToBase64String(imageBytes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a memory stream from an image.
|
||||
/// </summary>
|
||||
/// <param name="image">The bitmap image</param>
|
||||
/// <param name="format">The format of the bitmap image</param>
|
||||
/// <returns>The memory stream</returns>
|
||||
protected Stream GetStream(Bitmap image, ImageFormat format)
|
||||
{
|
||||
Stream stream = new MemoryStream();
|
||||
image.Save(stream, format);
|
||||
return stream;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the bytes from an image.
|
||||
/// </summary>
|
||||
/// <param name="image">The bitmap image</param>
|
||||
/// <returns>The bytes of the image</returns>
|
||||
protected byte[] GetBytes(Bitmap image)
|
||||
{
|
||||
ImageConverter converter = new ImageConverter();
|
||||
return (byte[])converter.ConvertTo(image, typeof(byte[]));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a synchronous POST request that returns a string.
|
||||
/// </summary>
|
||||
/// <param name="client">The HttpClient that performs the async request</param>
|
||||
/// <param name="url">The URL to call</param>
|
||||
/// <param name="content">The content that needs to be posted</param>
|
||||
/// <returns>The response as a string</returns>
|
||||
protected string PostSync(HttpClient client, string url, HttpContent content)
|
||||
{
|
||||
return Task.Run(() =>
|
||||
Task.Run(() =>
|
||||
client.PostAsync(url, content))
|
||||
.Result.Content.ReadAsStringAsync())
|
||||
.Result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a synchronous POST request that returns a byte array.
|
||||
/// </summary>
|
||||
/// <param name="client">The HttpClient that performs the async request</param>
|
||||
/// <param name="url">The URL to call</param>
|
||||
/// <param name="content">The content that needs to be posted</param>
|
||||
/// <returns>The response as a byte array</returns>
|
||||
protected byte[] PostSync2(HttpClient client, string url, HttpContent content)
|
||||
{
|
||||
return Task.Run(() =>
|
||||
Task.Run(() =>
|
||||
client.PostAsync(url, content))
|
||||
.Result.Content.ReadAsByteArrayAsync())
|
||||
.Result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a synchronous POST request that returns a stream.
|
||||
/// </summary>
|
||||
/// <param name="client">The HttpClient that performs the async request</param>
|
||||
/// <param name="url">The URL to call</param>
|
||||
/// <param name="content">The content that needs to be posted</param>
|
||||
/// <returns>The response as a stream</returns>
|
||||
protected Stream PostSync3(HttpClient client, string url, HttpContent content)
|
||||
{
|
||||
return Task.Run(() =>
|
||||
Task.Run(() =>
|
||||
client.PostAsync(url, content))
|
||||
.Result.Content.ReadAsStreamAsync())
|
||||
.Result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.Globalization;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
|
||||
namespace RuriLib.CaptchaServices
|
||||
{
|
||||
/// <summary>
|
||||
/// Implements the API of https://captchas.io/
|
||||
/// </summary>
|
||||
public class CaptchasIO : CaptchaService
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a CaptchasIO client.
|
||||
/// </summary>
|
||||
/// <param name="apiKey">The CaptchasIO API key</param>
|
||||
/// <param name="timeout">The maximum time to wait for the challenge to be solved</param>
|
||||
public CaptchasIO(string apiKey, int timeout) : base(apiKey, timeout) { }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override double GetBalance()
|
||||
{
|
||||
using (CWebClient client = new CWebClient())
|
||||
{
|
||||
if (Timeout > 0) client.Timeout = Timeout;
|
||||
var response = client.DownloadString($"http://api.captchas.io/res.php?key={ApiKey}&action=getbalance&json=1");
|
||||
GenericResponse gbr = JsonConvert.DeserializeObject<GenericResponse>(response);
|
||||
if (gbr.status == 0) throw new Exception(gbr.request);
|
||||
return double.Parse(gbr.request, CultureInfo.InvariantCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string SolveRecaptcha(string siteKey, string siteUrl)
|
||||
{
|
||||
// Create task
|
||||
using (CWebClient client = new CWebClient())
|
||||
{
|
||||
if (Timeout > 0) client.Timeout = Timeout;
|
||||
var response = client.DownloadString($"http://api.captchas.io/in.php?key={ApiKey}&method=userrecaptcha&googlekey={siteKey}&pageurl={siteUrl}&json=1");
|
||||
GenericResponse ctr = JsonConvert.DeserializeObject<GenericResponse>(response);
|
||||
if (ctr.status == 0) throw new Exception(ctr.request);
|
||||
TaskId = ctr.request;
|
||||
Status = CaptchaStatus.Processing;
|
||||
|
||||
// Check if task has been completed
|
||||
DateTime start = DateTime.Now;
|
||||
while (Status == CaptchaStatus.Processing && (DateTime.Now - start).TotalSeconds < Timeout)
|
||||
{
|
||||
Thread.Sleep(5000);
|
||||
response = client.DownloadString($"http://api.captchas.io/res.php?key={ApiKey}&action=get&id={TaskId}&json=1");
|
||||
GenericResponse gtrr = JsonConvert.DeserializeObject<GenericResponse>(response);
|
||||
if (gtrr.request.Contains("NOT_READY")) continue;
|
||||
if (gtrr.status == 0) throw new Exception(gtrr.request);
|
||||
Status = CaptchaStatus.Completed;
|
||||
return gtrr.request;
|
||||
}
|
||||
throw new TimeoutException();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string SolveCaptcha(Bitmap bitmap)
|
||||
{
|
||||
// Create task
|
||||
using (CWebClient client = new CWebClient())
|
||||
{
|
||||
if (Timeout > 0) client.Timeout = Timeout;
|
||||
client.Headers.Add(HttpRequestHeader.ContentType, "application/x-www-form-urlencoded");
|
||||
var response = client.UploadString("http://api.captchas.io.com/in.php",
|
||||
$"key={ApiKey}&method=base64®sense=1&body={GetBase64(bitmap, ImageFormat.Bmp)}&json=1");
|
||||
GenericResponse ctr = JsonConvert.DeserializeObject<GenericResponse>(response);
|
||||
if (ctr.status == 0) throw new Exception(ctr.request);
|
||||
TaskId = ctr.request;
|
||||
Status = CaptchaStatus.Processing;
|
||||
|
||||
// Check if task has been completed
|
||||
DateTime start = DateTime.Now;
|
||||
while (Status == CaptchaStatus.Processing && (DateTime.Now - start).TotalSeconds < Timeout)
|
||||
{
|
||||
Thread.Sleep(5000);
|
||||
response = client.DownloadString($"http://api.captchas.io/res.php?key={ApiKey}&action=get&id={TaskId}&json=1");
|
||||
GenericResponse gtrr = JsonConvert.DeserializeObject<GenericResponse>(response);
|
||||
if (gtrr.request.Contains("NOT_READY")) continue;
|
||||
if (gtrr.status == 0) throw new Exception(gtrr.request);
|
||||
Status = CaptchaStatus.Completed;
|
||||
return gtrr.request;
|
||||
}
|
||||
throw new TimeoutException();
|
||||
}
|
||||
}
|
||||
|
||||
#pragma warning disable 0414
|
||||
#pragma warning disable 0649
|
||||
struct GenericResponse
|
||||
{
|
||||
public int? status;
|
||||
public string request;
|
||||
}
|
||||
#pragma warning restore 0414
|
||||
#pragma warning restore 0649
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.Globalization;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace RuriLib.CaptchaServices
|
||||
{
|
||||
/// <summary>
|
||||
/// Implements the API of http://de-captcher.com/
|
||||
/// </summary>
|
||||
public class DeCaptcher : CaptchaService
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a DeCaptcher client.
|
||||
/// </summary>
|
||||
/// <param name="user">The DeCaptcher username</param>
|
||||
/// <param name="pass">The DeCaptcher password</param>
|
||||
/// <param name="timeout">The maximum time to wait for the challenge to be solved</param>
|
||||
public DeCaptcher(string user, string pass, int timeout) : base(user, pass, timeout) { }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override double GetBalance()
|
||||
{
|
||||
using (HttpClient client = new HttpClient())
|
||||
using (MultipartFormDataContent data = new MultipartFormDataContent())
|
||||
{
|
||||
client.Timeout = new TimeSpan(0, 0, Timeout);
|
||||
data.Add(new StringContent(User), "username");
|
||||
data.Add(new StringContent(Pass), "password");
|
||||
data.Add(new StringContent("balance"), "function");
|
||||
var response = PostSync(client, "http://poster.de-captcher.com/", data);
|
||||
return double.Parse(response, CultureInfo.InvariantCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string SolveRecaptcha(string siteKey, string siteUrl)
|
||||
{
|
||||
using (HttpClient client = new HttpClient())
|
||||
using (MultipartFormDataContent data = new MultipartFormDataContent())
|
||||
{
|
||||
client.Timeout = new TimeSpan(0, 0, Timeout);
|
||||
data.Add(new StringContent(User), "username");
|
||||
data.Add(new StringContent(Pass), "password");
|
||||
data.Add(new StringContent("proxyurl"), "function");
|
||||
data.Add(new StringContent(siteKey), "key");
|
||||
data.Add(new StringContent(siteUrl), "url");
|
||||
var response = PostSync(client, "http://poster.de-captcher.com/", data);
|
||||
try { return response.Split('|')[5]; }
|
||||
catch { throw new Exception(response); }
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string SolveCaptcha(Bitmap bitmap)
|
||||
{
|
||||
using(HttpClient client = new HttpClient())
|
||||
using (MultipartFormDataContent data = new MultipartFormDataContent())
|
||||
{
|
||||
client.Timeout = new TimeSpan(0, 0, Timeout);
|
||||
data.Add(new StringContent(User), "username");
|
||||
data.Add(new StringContent(Pass), "password");
|
||||
data.Add(new StringContent("picture2"), "function");
|
||||
data.Add(new ByteArrayContent(GetBytes(bitmap)), "pict");
|
||||
data.Add(new StringContent("0"), "picttype");
|
||||
var response = PostSync(client, "http://poster.de-captcher.com/", data);
|
||||
try { return response.Split('|')[5]; }
|
||||
catch { throw new Exception(response); }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
using Extreme.Net;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace RuriLib.CaptchaServices
|
||||
{
|
||||
/// <summary>
|
||||
/// Implements the API of https://www.deathbycaptcha.com/
|
||||
/// </summary>
|
||||
public class DeathByCaptcha : CaptchaService
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a DeathByCaptcha client.
|
||||
/// </summary>
|
||||
/// <param name="user">The DeathByCaptcha username</param>
|
||||
/// <param name="pass">The DeathByCaptcha password</param>
|
||||
/// <param name="timeout">The maximum time to wait for the challenge to be solved</param>
|
||||
public DeathByCaptcha(string user, string pass, int timeout) : base(user, pass, timeout) { }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override double GetBalance()
|
||||
{
|
||||
using (CWebClient client = new CWebClient())
|
||||
{
|
||||
if (Timeout > 0) client.Timeout = Timeout;
|
||||
client.Headers.Add(HttpRequestHeader.Accept, "application/json");
|
||||
client.Headers.Add(HttpRequestHeader.ContentType, "application/x-www-form-urlencoded");
|
||||
var response = client.UploadString("http://api.dbcapi.me/api/user", $"username={User}&password={Pass}");
|
||||
GetBalanceResponse gbr = JsonConvert.DeserializeObject<GetBalanceResponse>(response);
|
||||
if (gbr.status != 0) throw new Exception("Invalid Credentials");
|
||||
return gbr.balance / 100;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string SolveRecaptcha(string siteKey, string siteUrl)
|
||||
{
|
||||
// Create task
|
||||
HttpRequest request = new HttpRequest();
|
||||
request.AddHeader(HttpHeader.Accept, "application/json");
|
||||
var content = new Extreme.Net.MultipartContent(BlockRequest.GenerateMultipartBoundary());
|
||||
content.Add(new Extreme.Net.StringContent(User), "username");
|
||||
content.Add(new Extreme.Net.StringContent(Pass), "password");
|
||||
content.Add(new Extreme.Net.StringContent("4"), "type");
|
||||
content.Add(new Extreme.Net.StringContent(JsonConvert.SerializeObject(new CreateRecaptchaTaskRequest(siteUrl, siteKey))), "token_params");
|
||||
var response = request.Post("http://api.dbcapi.me/api/captcha", content).ToString();
|
||||
var split = response.Split('&');
|
||||
var status = int.Parse(split[0].Split('=')[1]);
|
||||
var id = split[1].Split('=')[1];
|
||||
if (status == 255) throw new Exception(response);
|
||||
TaskId = id;
|
||||
Status = CaptchaStatus.Processing;
|
||||
|
||||
// Check if task has been completed
|
||||
DateTime start = DateTime.Now;
|
||||
while (Status == CaptchaStatus.Processing && (DateTime.Now - start).TotalSeconds < Timeout)
|
||||
{
|
||||
Thread.Sleep(5000);
|
||||
HttpRequest gRequest = new HttpRequest();
|
||||
gRequest.AddHeader(HttpHeader.Accept, "application/json");
|
||||
HttpResponse gResponse = gRequest.Get($"http://api.dbcapi.me/api/captcha/{TaskId}");
|
||||
var resp = gResponse.ToString();
|
||||
CreateTaskResponse gtrr = JsonConvert.DeserializeObject<CreateTaskResponse>(resp);
|
||||
if (gtrr == null) continue;
|
||||
if (!gtrr.is_correct) throw new Exception("No answer could be found");
|
||||
if (gtrr.text != "")
|
||||
{
|
||||
Status = CaptchaStatus.Completed;
|
||||
return gtrr.text;
|
||||
}
|
||||
}
|
||||
throw new TimeoutException();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string SolveCaptcha(Bitmap bitmap)
|
||||
{
|
||||
// Create task
|
||||
HttpRequest request = new HttpRequest();
|
||||
request.AddHeader(HttpHeader.Accept, "application/json");
|
||||
Extreme.Net.MultipartContent content = new Extreme.Net.MultipartContent(BlockRequest.GenerateMultipartBoundary());
|
||||
content.Add(new Extreme.Net.StringContent(User), "username");
|
||||
content.Add(new Extreme.Net.StringContent(Pass), "password");
|
||||
content.Add(new Extreme.Net.StringContent($"base64:{GetBase64(bitmap, ImageFormat.Jpeg)}"), "captchafile");
|
||||
var response = request.Post("http://api.dbcapi.me/api/captcha", content).ToString();
|
||||
var split = response.Split('&');
|
||||
var status = int.Parse(split[0].Split('=')[1]);
|
||||
var id = split[1].Split('=')[1];
|
||||
if (status == 255) throw new Exception(response);
|
||||
TaskId = id;
|
||||
Status = CaptchaStatus.Processing;
|
||||
|
||||
// Check if task has been completed
|
||||
DateTime start = DateTime.Now;
|
||||
while (Status == CaptchaStatus.Processing && (DateTime.Now - start).TotalSeconds < Timeout)
|
||||
{
|
||||
Thread.Sleep(5000);
|
||||
HttpRequest gRequest = new HttpRequest();
|
||||
gRequest.AddHeader(HttpHeader.Accept, "application/json");
|
||||
HttpResponse gResponse = gRequest.Get($"http://api.dbcapi.me/api/captcha/{TaskId}");
|
||||
var resp = gResponse.ToString();
|
||||
CreateTaskResponse gtrr = JsonConvert.DeserializeObject<CreateTaskResponse>(resp);
|
||||
if (gtrr == null) continue;
|
||||
if (!gtrr.is_correct) throw new Exception("No answer could be found");
|
||||
if (gtrr.text != "")
|
||||
{
|
||||
Status = CaptchaStatus.Completed;
|
||||
return gtrr.text;
|
||||
}
|
||||
}
|
||||
throw new TimeoutException();
|
||||
}
|
||||
#pragma warning disable 0649
|
||||
#pragma warning disable 0414
|
||||
class GetBalanceResponse
|
||||
{
|
||||
public int status;
|
||||
public double balance;
|
||||
}
|
||||
|
||||
class CreateRecaptchaTaskRequest
|
||||
{
|
||||
public string googlekey;
|
||||
public string pageurl;
|
||||
public CreateRecaptchaTaskRequest(string url, string key) { pageurl = url; googlekey = key; }
|
||||
}
|
||||
|
||||
class CreateCaptchaTaskRequest
|
||||
{
|
||||
public string clientKey;
|
||||
public CaptchaTask task;
|
||||
public CreateCaptchaTaskRequest(string apiKey, string base64) { clientKey = apiKey; task = new CaptchaTask(base64); }
|
||||
}
|
||||
|
||||
class CaptchaTask
|
||||
{
|
||||
public string type = "ImageToTextTask";
|
||||
public string body;
|
||||
public CaptchaTask(string base64) { body = base64; }
|
||||
}
|
||||
|
||||
class CreateTaskResponse
|
||||
{
|
||||
public int captcha;
|
||||
public int status;
|
||||
public bool is_correct;
|
||||
public string text;
|
||||
public string error;
|
||||
}
|
||||
|
||||
class GetTaskResultRequest
|
||||
{
|
||||
public string clientKey;
|
||||
public int taskId;
|
||||
public GetTaskResultRequest(string apiKey, int id) { clientKey = apiKey; taskId = id; }
|
||||
}
|
||||
|
||||
class GetTaskResultCaptchaResponse
|
||||
{
|
||||
public int errorId;
|
||||
public string errorCode;
|
||||
public string status;
|
||||
public CaptchaSolution solution;
|
||||
}
|
||||
|
||||
class GetTaskResultRecaptchaResponse
|
||||
{
|
||||
public int errorId;
|
||||
public string errorCode;
|
||||
public string status;
|
||||
public RecaptchaSolution solution;
|
||||
}
|
||||
|
||||
class RecaptchaSolution
|
||||
{
|
||||
public string gRecaptchaResponse;
|
||||
}
|
||||
|
||||
class CaptchaSolution
|
||||
{
|
||||
public string text;
|
||||
}
|
||||
|
||||
#pragma warning restore 0414
|
||||
#pragma warning restore 0649
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.Globalization;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
|
||||
namespace RuriLib.CaptchaServices
|
||||
{
|
||||
/// <summary>
|
||||
/// Implements the API of http://www.imagetyperz.com/
|
||||
/// </summary>
|
||||
public class ImageTyperz : CaptchaService
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates an ImageTyperz client.
|
||||
/// </summary>
|
||||
/// <param name="apiKey">The ImageTyperz API key</param>
|
||||
/// <param name="timeout">The maximum time to wait for the challenge to be solved</param>
|
||||
public ImageTyperz(string apiKey, int timeout) : base(apiKey, timeout) { }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override double GetBalance()
|
||||
{
|
||||
using (CWebClient client = new CWebClient())
|
||||
{
|
||||
if (Timeout > 0) client.Timeout = Timeout;
|
||||
client.Headers.Add("Content-Type", "application/x-www-form-urlencoded");
|
||||
var res = client.UploadString("http://captchatypers.com/Forms/RequestBalanceToken.ashx", $"token={ApiKey}&action=REQUESTBALANCE");
|
||||
if (res.Contains("ERROR")) throw new Exception(res);
|
||||
return double.Parse(res, CultureInfo.InvariantCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string SolveRecaptcha(string siteKey, string siteUrl)
|
||||
{
|
||||
// Create task
|
||||
using (CWebClient client = new CWebClient())
|
||||
{
|
||||
if (Timeout > 0) client.Timeout = Timeout;
|
||||
client.Headers.Add(HttpRequestHeader.ContentType, "application/x-www-form-urlencoded");
|
||||
var res = client.UploadString("http://captchatypers.com/captchaapi/UploadRecaptchaToken.ashx",
|
||||
$"token={ApiKey}&action=UPLOADCAPTCHA&pageurl={siteUrl}&googlekey={siteKey}");
|
||||
if (res.Contains("ERROR")) throw new Exception(res);
|
||||
TaskId = res;
|
||||
Status = CaptchaStatus.Processing;
|
||||
|
||||
// Check if task has been completed
|
||||
DateTime start = DateTime.Now;
|
||||
while (Status == CaptchaStatus.Processing && (DateTime.Now - start).TotalSeconds < Timeout)
|
||||
{
|
||||
Thread.Sleep(5000);
|
||||
client.Headers.Add(HttpRequestHeader.ContentType, "application/x-www-form-urlencoded");
|
||||
res = client.UploadString("http://captchatypers.com/captchaapi/GetRecaptchaTextToken.ashx",
|
||||
$"token={ApiKey}&action=GETTEXT&captchaID={TaskId}");
|
||||
if (res.Contains("NOTDECODED")) continue;
|
||||
if (res.Contains("ERROR")) throw new Exception(res);
|
||||
Status = CaptchaStatus.Completed;
|
||||
return res;
|
||||
}
|
||||
throw new TimeoutException();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string SolveCaptcha(Bitmap bitmap)
|
||||
{
|
||||
// Create task
|
||||
using (CWebClient client = new CWebClient())
|
||||
{
|
||||
if (Timeout > 0) client.Timeout = Timeout;
|
||||
client.Headers.Add(HttpRequestHeader.ContentType, "application/x-www-form-urlencoded");
|
||||
var res = client.UploadString("http://captchatypers.com/Forms/UploadFileAndGetTextNEWToken.ashx",
|
||||
$"token={ApiKey}&action=UPLOADCAPTCHA&chkCase=1&file={WebUtility.UrlEncode(GetBase64(bitmap, ImageFormat.Bmp))}");
|
||||
if (res.Contains("ERROR")) throw new Exception(res);
|
||||
Status = CaptchaStatus.Completed;
|
||||
return res.Split('|')[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.Globalization;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
|
||||
namespace RuriLib.CaptchaServices
|
||||
{
|
||||
/// <summary>
|
||||
/// Implements the API of https://www.solverecaptcha.com/
|
||||
/// </summary>
|
||||
public class SolveReCaptcha : CaptchaService
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a SolveRecaptcha client.
|
||||
/// </summary>
|
||||
/// <param name="userId">The id of the user</param>
|
||||
/// <param name="apiKey">The SolveRecaptcha API key</param>
|
||||
/// <param name="timeout">The maximum time to wait for the challenge to be solved</param>
|
||||
public SolveReCaptcha(string userId, string apiKey, int timeout) : base(userId, apiKey, timeout) { }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string SolveRecaptcha(string siteKey, string siteUrl)
|
||||
{
|
||||
// Create task
|
||||
using (CWebClient client = new CWebClient())
|
||||
{
|
||||
if (Timeout > 0) client.Timeout = Timeout;
|
||||
var response = client.DownloadString($"https://api.solverecaptcha.com/?userid={User}&key={Pass}&sitekey={siteKey}&pageurl={siteUrl}");
|
||||
if (response.Contains("ERROR")) throw new Exception(response);
|
||||
Status = CaptchaStatus.Completed;
|
||||
return response.Split('|')[1];
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override double GetBalance()
|
||||
{
|
||||
// Create task
|
||||
using (CWebClient client = new CWebClient())
|
||||
{
|
||||
client.Timeout = 3;
|
||||
try
|
||||
{
|
||||
var response = client.DownloadString($"https://api.solverecaptcha.com/?userid={User}&key={Pass}&sitekey=test&pageurl=test");
|
||||
if (response.Contains("ERROR_ACCESS_DENIED")) return 0; // Invalid key
|
||||
}
|
||||
catch { } // Catch the timeout exception, this means it's trying to process a captcha so the key is valid
|
||||
return 999;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.Globalization;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
|
||||
namespace RuriLib.CaptchaServices
|
||||
{
|
||||
/// <summary>
|
||||
/// Implements the API of https://2captcha.com
|
||||
/// </summary>
|
||||
public class TwoCaptcha : CaptchaService
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a 2Captcha client.
|
||||
/// </summary>
|
||||
/// <param name="apiKey">The 2Captcha API key</param>
|
||||
/// <param name="timeout">The maximum time to wait for the challenge to be solved</param>
|
||||
public TwoCaptcha(string apiKey, int timeout) : base(apiKey, timeout) { }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override double GetBalance()
|
||||
{
|
||||
using (CWebClient client = new CWebClient())
|
||||
{
|
||||
if (Timeout > 0) client.Timeout = Timeout;
|
||||
var response = client.DownloadString($"http://2captcha.com/res.php?key={ApiKey}&action=getbalance&json=1");
|
||||
GenericResponse gbr = JsonConvert.DeserializeObject<GenericResponse>(response);
|
||||
if (gbr.status == 0) throw new Exception(gbr.request);
|
||||
return double.Parse(gbr.request, CultureInfo.InvariantCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string SolveRecaptcha(string siteKey, string siteUrl)
|
||||
{
|
||||
// Create task
|
||||
using (CWebClient client = new CWebClient())
|
||||
{
|
||||
if (Timeout > 0) client.Timeout = Timeout;
|
||||
var response = client.DownloadString($"http://2captcha.com/in.php?key={ApiKey}&method=userrecaptcha&googlekey={siteKey}&pageurl={siteUrl}&json=1");
|
||||
GenericResponse ctr = JsonConvert.DeserializeObject<GenericResponse>(response);
|
||||
if (ctr.status == 0) throw new Exception(ctr.request);
|
||||
TaskId = ctr.request;
|
||||
Status = CaptchaStatus.Processing;
|
||||
|
||||
// Check if task has been completed
|
||||
DateTime start = DateTime.Now;
|
||||
while (Status == CaptchaStatus.Processing && (DateTime.Now - start).TotalSeconds < Timeout)
|
||||
{
|
||||
Thread.Sleep(5000);
|
||||
response = client.DownloadString($"http://2captcha.com/res.php?key={ApiKey}&action=get&id={TaskId}&json=1");
|
||||
GenericResponse gtrr = JsonConvert.DeserializeObject<GenericResponse>(response);
|
||||
if (gtrr.request.Contains("NOTREADY")) continue;
|
||||
if (gtrr.status == 0) throw new Exception(gtrr.request);
|
||||
Status = CaptchaStatus.Completed;
|
||||
return gtrr.request;
|
||||
}
|
||||
throw new TimeoutException();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string SolveCaptcha(Bitmap bitmap)
|
||||
{
|
||||
// Create task
|
||||
using (CWebClient client = new CWebClient())
|
||||
{
|
||||
if (Timeout > 0) client.Timeout = Timeout;
|
||||
client.Headers.Add(HttpRequestHeader.ContentType, "application/x-www-form-urlencoded");
|
||||
var response = client.UploadString("http://2captcha.com/in.php",
|
||||
$"key={ApiKey}&method=base64®sense=1&body={GetBase64(bitmap, ImageFormat.Bmp)}&json=1");
|
||||
GenericResponse ctr = JsonConvert.DeserializeObject<GenericResponse>(response);
|
||||
if (ctr.status == 0) throw new Exception(ctr.request);
|
||||
TaskId = ctr.request;
|
||||
Status = CaptchaStatus.Processing;
|
||||
|
||||
// Check if task has been completed
|
||||
DateTime start = DateTime.Now;
|
||||
while (Status == CaptchaStatus.Processing && (DateTime.Now - start).TotalSeconds < Timeout)
|
||||
{
|
||||
Thread.Sleep(5000);
|
||||
response = client.DownloadString($"http://2captcha.com/res.php?key={ApiKey}&action=get&id={TaskId}&json=1");
|
||||
GenericResponse gtrr = JsonConvert.DeserializeObject<GenericResponse>(response);
|
||||
if (gtrr.request.Contains("NOTREADY")) continue;
|
||||
if (gtrr.status == 0) throw new Exception(gtrr.request);
|
||||
Status = CaptchaStatus.Completed;
|
||||
return gtrr.request;
|
||||
}
|
||||
throw new TimeoutException();
|
||||
}
|
||||
}
|
||||
|
||||
#pragma warning disable 0414
|
||||
#pragma warning disable 0649
|
||||
class GenericResponse
|
||||
{
|
||||
public int status;
|
||||
public string request;
|
||||
}
|
||||
#pragma warning restore 0414
|
||||
#pragma warning restore 0649
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
|
||||
namespace RuriLib
|
||||
{
|
||||
/// <summary>
|
||||
/// The condition on which to base the outcome of a comparison.
|
||||
/// </summary>
|
||||
public enum Condition
|
||||
{
|
||||
/// <summary>A is less than B.</summary>
|
||||
LessThan,
|
||||
|
||||
/// <summary>A is greater than B.</summary>
|
||||
GreaterThan,
|
||||
|
||||
/// <summary>A is equal to B.</summary>
|
||||
EqualTo,
|
||||
|
||||
/// <summary>A is not equal to B.</summary>
|
||||
NotEqualTo,
|
||||
|
||||
/// <summary>A contains B.</summary>
|
||||
Contains,
|
||||
|
||||
/// <summary>A does not contain B.</summary>
|
||||
DoesNotContain,
|
||||
|
||||
/// <summary>Whether any variable can be replaced inside the string.</summary>
|
||||
Exists
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Static Class used to check if a condition is true or false.
|
||||
/// </summary>
|
||||
public static class ConditionChecker
|
||||
{
|
||||
/// <summary>
|
||||
/// Verifies if a comparison is true or false.
|
||||
/// </summary>
|
||||
/// <param name="left">The left-hand term in the comparison</param>
|
||||
/// <param name="condition">The condition of the comparison</param>
|
||||
/// <param name="right">The right-hand term in the comparison</param>
|
||||
/// <param name="data">The BotData used for variable replacement</param>
|
||||
/// <returns>Whether the comparison is true or false</returns>
|
||||
public static bool Verify(string left, Condition condition, string right, BotData data)
|
||||
{
|
||||
var style = NumberStyles.Number | NumberStyles.AllowCurrencySymbol; // Needed when comparing values with a currency symbol
|
||||
var provider = new CultureInfo("en-US");
|
||||
var L = BlockBase.ReplaceValuesRecursive(left, data); // The left-hand term can accept recursive values like <LIST[*]>
|
||||
var r = BlockBase.ReplaceValues(right, data); // The right-hand term cannot
|
||||
|
||||
switch (condition)
|
||||
{
|
||||
case Condition.EqualTo:
|
||||
return L.Any(l => l == r);
|
||||
|
||||
case Condition.NotEqualTo:
|
||||
return L.Any(l => l != r);
|
||||
|
||||
case Condition.GreaterThan:
|
||||
return L.Any(l => decimal.Parse(l.Replace(',', '.'), style, provider) > decimal.Parse(r, style, provider));
|
||||
|
||||
case Condition.LessThan:
|
||||
return L.Any(l => decimal.Parse(l.Replace(',', '.'), style, provider) < decimal.Parse(r, style, provider));
|
||||
|
||||
case Condition.Contains:
|
||||
return L.Any(l => l.Contains(r));
|
||||
|
||||
case Condition.DoesNotContain:
|
||||
return L.Any(l => !l.Contains(r));
|
||||
|
||||
case Condition.Exists:
|
||||
return L.Any(l => l != left); // Returns true if any replacement took place
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,260 @@
|
||||
using Newtonsoft.Json;
|
||||
using RuriLib.Models;
|
||||
using RuriLib.ViewModels;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace RuriLib
|
||||
{
|
||||
/// <summary>
|
||||
/// Static Class used to access serialization and deserialization of objects.
|
||||
/// </summary>
|
||||
public static class IOManager
|
||||
{
|
||||
private static JsonSerializerSettings settings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All };
|
||||
|
||||
/// <summary>
|
||||
/// Saves the RuriLib settings to a file.
|
||||
/// </summary>
|
||||
/// <param name="settingsFile">The file you want to save to</param>
|
||||
/// <param name="settings">The RuriLib settings object</param>
|
||||
public static void SaveSettings(string settingsFile, RLSettingsViewModel settings)
|
||||
{
|
||||
File.WriteAllText(settingsFile, JsonConvert.SerializeObject(settings, Formatting.Indented));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads the RuriLib settings from a file.
|
||||
/// </summary>
|
||||
/// <param name="settingsFile">The file you want to load from</param>
|
||||
/// <returns>An instance of RLSettingsViewModel</returns>
|
||||
public static RLSettingsViewModel LoadSettings(string settingsFile)
|
||||
{
|
||||
return JsonConvert.DeserializeObject<RLSettingsViewModel>(File.ReadAllText(settingsFile));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serializes a block to a JSON string.
|
||||
/// </summary>
|
||||
/// <param name="block">The block to serialize</param>
|
||||
/// <returns>The JSON-encoded BlockBase object with TypeNameHandling on</returns>
|
||||
public static string SerializeBlock(BlockBase block)
|
||||
{
|
||||
return JsonConvert.SerializeObject(block, Formatting.None, settings);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deserializes a Block from a JSON string.
|
||||
/// </summary>
|
||||
/// <param name="block">The JSON-encoded string with TypeNameHandling on</param>
|
||||
/// <returns>An instance of BlockBase</returns>
|
||||
public static BlockBase DeserializeBlock(string block)
|
||||
{
|
||||
return JsonConvert.DeserializeObject<BlockBase>(block, settings);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serializes a list of blocks to a JSON string.
|
||||
/// </summary>
|
||||
/// <param name="blocks">The list of blocks to serialize</param>
|
||||
/// <returns>The JSON-encoded List of BlockBase objects with TypeNameHandling on</returns>
|
||||
public static string SerializeBlocks(List<BlockBase> blocks)
|
||||
{
|
||||
return JsonConvert.SerializeObject(blocks, Formatting.None, settings);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deserializes a list of blocks from a JSON string.
|
||||
/// </summary>
|
||||
/// <param name="blocks">The JSON-encoded string with TypeNameHandling on</param>
|
||||
/// <returns>A list of instances of BlockBase</returns>
|
||||
public static List<BlockBase> DeserializeBlocks(string blocks)
|
||||
{
|
||||
return JsonConvert.DeserializeObject<List<BlockBase>>(blocks, settings);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serializes a Config object to the loli-formatted string.
|
||||
/// </summary>
|
||||
/// <param name="config">The Config to serialize</param>
|
||||
/// <returns>The loli-formatted string</returns>
|
||||
public static string SerializeConfig(Config config)
|
||||
{
|
||||
StringWriter writer = new StringWriter();
|
||||
writer.WriteLine("[SETTINGS]");
|
||||
writer.WriteLine(JsonConvert.SerializeObject(config.Settings, Formatting.Indented));
|
||||
writer.WriteLine("");
|
||||
writer.WriteLine("[SCRIPT]");
|
||||
writer.Write(config.Script);
|
||||
return writer.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deserializes a Config object from a loli-formatted string.
|
||||
/// </summary>
|
||||
/// <param name="config">The loli-formatted string</param>
|
||||
/// <returns>An instance of the Config object</returns>
|
||||
public static Config DeserializeConfig(string config)
|
||||
{
|
||||
var split = config.Split(new string[] { "[SETTINGS]", "[SCRIPT]" }, StringSplitOptions.RemoveEmptyEntries);
|
||||
return new Config(JsonConvert.DeserializeObject<ConfigSettings>(split[0]), split[1].TrimStart('\r', '\n'));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serializes a list of proxies to a JSON string.
|
||||
/// </summary>
|
||||
/// <param name="proxies">The list of proxies to serialize</param>
|
||||
/// <returns>The JSON-encoded list of CProxy objects with TypeNameHandling on</returns>
|
||||
public static string SerializeProxies(List<CProxy> proxies)
|
||||
{
|
||||
return JsonConvert.SerializeObject(proxies, Formatting.None);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deserializes a list of proxies from a JSON string.
|
||||
/// </summary>
|
||||
/// <param name="proxies">The JSON-encoded list of proxies with TypeNameHandling on</param>
|
||||
/// <returns>A list of CProxy objects</returns>
|
||||
public static List<CProxy> DeserializeProxies(string proxies)
|
||||
{
|
||||
return JsonConvert.DeserializeObject<List<CProxy>>(proxies);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads a Config object from a .loli file.
|
||||
/// </summary>
|
||||
/// <param name="fileName">The config file</param>
|
||||
/// <returns>A Config object</returns>
|
||||
public static Config LoadConfig(string fileName)
|
||||
{
|
||||
return DeserializeConfig(File.ReadAllText(fileName));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves a Config object to a .loli file.
|
||||
/// </summary>
|
||||
/// <param name="config">The viewmodel of the config to save</param>
|
||||
/// <param name="fileName">The path of the file where the Config will be saved</param>
|
||||
/// <returns>Whether the file has been saved successfully</returns>
|
||||
public static bool SaveConfig(Config config, string fileName)
|
||||
{
|
||||
try
|
||||
{
|
||||
File.WriteAllText(fileName, SerializeConfig(config));
|
||||
return true;
|
||||
}
|
||||
catch { return false; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clones a Config object by serializing and deserializing it.
|
||||
/// </summary>
|
||||
/// <param name="config">The object to clone</param>
|
||||
/// <returns>The cloned Config object</returns>
|
||||
public static Config CloneConfig(Config config)
|
||||
{
|
||||
return DeserializeConfig(SerializeConfig(config));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clones a BlockBase object by serializing and deserializing it.
|
||||
/// </summary>
|
||||
/// <param name="block">The object to clone</param>
|
||||
/// <returns>The cloned BlockBase object</returns>
|
||||
public static BlockBase CloneBlock(BlockBase block)
|
||||
{
|
||||
return DeserializeBlock(SerializeBlock(block));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clones a list of proxies by serializing and deserializing it.
|
||||
/// </summary>
|
||||
/// <param name="proxies">The list of proxies to clone</param>
|
||||
/// <returns>The cloned list of proxies</returns>
|
||||
public static List<CProxy> CloneProxies(List<CProxy> proxies)
|
||||
{
|
||||
return DeserializeProxies(SerializeProxies(proxies));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses the EnvironmentSettings from a file.
|
||||
/// </summary>
|
||||
/// <param name="envFile">The .ini file of the settings</param>
|
||||
/// <returns>The loaded EnvironmentSettings object</returns>
|
||||
public static EnvironmentSettings ParseEnvironmentSettings(string envFile)
|
||||
{
|
||||
var env = new EnvironmentSettings();
|
||||
var lines = File.ReadAllLines(envFile).Where(l => !string.IsNullOrEmpty(l)).ToArray();
|
||||
for (int i = 0; i < lines.Count(); i++)
|
||||
{
|
||||
var line = lines[i];
|
||||
if (line.StartsWith("#")) continue;
|
||||
else if (line.StartsWith("["))
|
||||
{
|
||||
Type type;
|
||||
var header = line;
|
||||
switch (line.Trim())
|
||||
{
|
||||
case "[WLTYPE]": type = typeof(WordlistType); break;
|
||||
case "[CUSTOMKC]": type = typeof(CustomKeychain); break;
|
||||
case "[EXPFORMAT]": type = typeof(ExportFormat); break;
|
||||
default: throw new Exception("Unrecognized ini header");
|
||||
}
|
||||
|
||||
var parameters = new List<string>();
|
||||
int j = i + 1;
|
||||
for (; j < lines.Count(); j++)
|
||||
{
|
||||
line = lines[j];
|
||||
if (line.StartsWith("[")) break;
|
||||
else if (line.Trim() == "" || line.StartsWith("#")) continue;
|
||||
else parameters.Add(line);
|
||||
}
|
||||
|
||||
switch (header)
|
||||
{
|
||||
case "[WLTYPE]": env.WordlistTypes.Add((WordlistType)ParseObjectFromIni(type, parameters)); break;
|
||||
case "[CUSTOMKC]": env.CustomKeychains.Add((CustomKeychain)ParseObjectFromIni(type, parameters)); break;
|
||||
case "[EXPFORMAT]": env.ExportFormats.Add((ExportFormat)ParseObjectFromIni(type, parameters)); break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
i = j - 1;
|
||||
}
|
||||
}
|
||||
|
||||
return env;
|
||||
}
|
||||
|
||||
private static object ParseObjectFromIni(Type type, List<string> parameters)
|
||||
{
|
||||
object obj = Activator.CreateInstance(type);
|
||||
foreach (var pair in parameters
|
||||
.Where(p => !string.IsNullOrEmpty(p))
|
||||
.Select(p => p.Split(new char[] { '=' }, 2)))
|
||||
{
|
||||
var prop = type.GetProperty(pair[0]);
|
||||
var propObj = prop.GetValue(obj);
|
||||
dynamic value = null;
|
||||
var ts = new TypeSwitch()
|
||||
.Case((String x) => value = pair[1])
|
||||
.Case((Int32 x) => value = Int32.Parse(pair[1]))
|
||||
.Case((Boolean x) => value = Boolean.Parse(pair[1]))
|
||||
.Case((List<String> x) => value = pair[1].Split(',').ToList())
|
||||
.Case((Color x) => value = Color.FromRgb(
|
||||
System.Drawing.Color.FromName(pair[1]).R,
|
||||
System.Drawing.Color.FromName(pair[1]).G,
|
||||
System.Drawing.Color.FromName(pair[1]).B
|
||||
))
|
||||
;
|
||||
|
||||
ts.Switch(propObj);
|
||||
prop.SetValue(obj, value);
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace RuriLib.LS
|
||||
{
|
||||
/// <summary>
|
||||
/// An exception thrown inside a block's Process method.
|
||||
/// </summary>
|
||||
public class BlockProcessingException : Exception
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates the exception without a message.
|
||||
/// </summary>
|
||||
public BlockProcessingException()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the exception with a message.
|
||||
/// </summary>
|
||||
/// <param name="message">The message</param>
|
||||
public BlockProcessingException(string message) : base(message)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the exception with a message and an inner exception.
|
||||
/// </summary>
|
||||
/// <param name="message">The message</param>
|
||||
/// <param name="inner">The inner exception</param>
|
||||
public BlockProcessingException(string message, Exception inner) : base(message, inner)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,552 @@
|
||||
using IronPython.Compiler;
|
||||
using IronPython.Hosting;
|
||||
using IronPython.Runtime;
|
||||
using Jint;
|
||||
using RuriLib.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace RuriLib.LS
|
||||
{
|
||||
/// <summary>
|
||||
/// The supported scripting languages.
|
||||
/// </summary>
|
||||
public enum ScriptingLanguage
|
||||
{
|
||||
/// <summary>The JavaScript language.</summary>
|
||||
JavaScript,
|
||||
|
||||
/// <summary>The Python language that interacts with the .NET framework.</summary>
|
||||
IronPython
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a LoliScript script that can be run line by line.
|
||||
/// </summary>
|
||||
public class LoliScript
|
||||
{
|
||||
/// <summary>The actual script as a string containing linebreaks.</summary>
|
||||
public string Script { get; set; }
|
||||
|
||||
/// <summary>The list of all lines of the script, where expanded blocks have been compressed into one-liners.</summary>
|
||||
private string[] CompressedLines
|
||||
{
|
||||
get
|
||||
{
|
||||
int i = 0;
|
||||
bool isScript = false;
|
||||
var compressed = Script.Split(new string[] { Environment.NewLine }, StringSplitOptions.None).ToList();
|
||||
while (i < compressed.Count - 1)
|
||||
{
|
||||
if (!isScript && BlockParser.IsBlock(compressed[i]) && (compressed[i + 1].StartsWith(" ") || compressed[i + 1].StartsWith("\t")))
|
||||
{
|
||||
compressed[i] += $" {compressed[i + 1].Trim()}";
|
||||
compressed.RemoveAt(i + 1);
|
||||
}
|
||||
else if (!isScript && BlockParser.IsBlock(compressed[i]) && (compressed[i + 1].StartsWith("! ") || compressed[i + 1].StartsWith("!\t")))
|
||||
{
|
||||
compressed[i] += $" {compressed[i + 1].Substring(1).Trim()}";
|
||||
compressed.RemoveAt(i + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (compressed[i].StartsWith("BEGIN SCRIPT")) isScript = true;
|
||||
else if (compressed[i].StartsWith("END SCRIPT")) isScript = false;
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
return compressed.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
// Runtime counter
|
||||
private int i = 0;
|
||||
private string[] lines = new string[] { };
|
||||
|
||||
// Needed for BEGIN SCRIPT directives
|
||||
private string otherScript = "";
|
||||
private ScriptingLanguage language = ScriptingLanguage.JavaScript;
|
||||
|
||||
/// <summary>The current line being processed.</summary>
|
||||
public string CurrentLine { get; set; } = "";
|
||||
|
||||
/// <summary>The current block being processed.</summary>
|
||||
public string CurrentBlock { get; set; } = "";
|
||||
|
||||
/// <summary>Whether the script can proceed the execution or not.</summary>
|
||||
public bool CanProceed
|
||||
{
|
||||
get
|
||||
{
|
||||
return i < lines.Count() && lines.Skip(i).Any(l => !IsEmptyOrCommentOrDisabled(l));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a LoliScript object with an empty script.
|
||||
/// </summary>
|
||||
public LoliScript()
|
||||
{
|
||||
Script = "";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a LoliScript object with a given script.
|
||||
/// </summary>
|
||||
/// <param name="script">The LoliScript script</param>
|
||||
public LoliScript(string script)
|
||||
{
|
||||
Script = script;
|
||||
}
|
||||
|
||||
#region Conversion
|
||||
/// <summary>
|
||||
/// Transforms the script into a list of blocks. The blocks that cannot be converted will be created as BlockLSCode blocks.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public List<BlockBase> ToBlocks()
|
||||
{
|
||||
List<BlockBase> list = new List<BlockBase>();
|
||||
var compressed = CompressedLines;
|
||||
List<string> buffer = new List<string>();
|
||||
var isScript = false;
|
||||
|
||||
foreach (var c in compressed.Where(c => !string.IsNullOrEmpty(c.Trim())))
|
||||
{
|
||||
if (!isScript && BlockParser.IsBlock(c))
|
||||
{
|
||||
if (buffer.Count > 0)
|
||||
{
|
||||
var b = new BlockLSCode();
|
||||
list.Add(b.FromLS(buffer));
|
||||
buffer.Clear();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
list.Add(BlockParser.Parse(c));
|
||||
}
|
||||
catch (Exception ex) {
|
||||
var line = c;
|
||||
line = BlockBase.TruncatePretty(line, 50);
|
||||
throw new Exception($"Exception while parsing block {line}\nReason: {ex.Message}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer.Add(c);
|
||||
|
||||
if (c.StartsWith("BEGIN SCRIPT")) isScript = true;
|
||||
else if (c.StartsWith("END SCRIPT")) isScript = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (buffer.Count > 0)
|
||||
{
|
||||
var b = new BlockLSCode();
|
||||
list.Add(b.FromLS(buffer));
|
||||
buffer.Clear();
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the script from a list of blocks.
|
||||
/// </summary>
|
||||
/// <param name="blocks">The list of blocks which inherit from the BlockBase type</param>
|
||||
public void FromBlocks(List<BlockBase> blocks)
|
||||
{
|
||||
Script = "";
|
||||
|
||||
foreach (var block in blocks)
|
||||
{
|
||||
Script += block.ToLS() + Environment.NewLine;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Resets the line counter so the script can be run again.
|
||||
/// </summary>
|
||||
public void Reset()
|
||||
{
|
||||
i = 0;
|
||||
otherScript = "";
|
||||
language = ScriptingLanguage.JavaScript;
|
||||
|
||||
// Separate the script into lines
|
||||
lines = Regex.Split(Script, "\r\n|\r|\n");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes a line of the script.
|
||||
/// </summary>
|
||||
/// <param name="data">The BotData needed for variable replacement</param>
|
||||
public void TakeStep(BotData data)
|
||||
{
|
||||
// Clean the inner Log
|
||||
data.LogBuffer.Clear();
|
||||
|
||||
if (data.Status != BotStatus.NONE && data.Status != BotStatus.SUCCESS)
|
||||
{
|
||||
i = lines.Count(); // Go to the end
|
||||
return;
|
||||
}
|
||||
|
||||
TAKELINE:
|
||||
|
||||
CurrentLine = lines[i];
|
||||
|
||||
// Skip comments and blank lines
|
||||
if (IsEmptyOrCommentOrDisabled(CurrentLine))
|
||||
{
|
||||
i++; // Go to the next
|
||||
goto TAKELINE;
|
||||
}
|
||||
|
||||
// Lookahead to compact lines. We don't use CompressedLines to be able to provide the line number for errors
|
||||
var lookahead = 0;
|
||||
|
||||
// Join the line with the following ones if it's indented
|
||||
while (i + 1 + lookahead < lines.Count())
|
||||
{
|
||||
var nextLine = lines[i + 1 + lookahead];
|
||||
if (nextLine.StartsWith(" ") || nextLine.StartsWith("\t"))
|
||||
CurrentLine += $" {nextLine.Trim()}";
|
||||
else break;
|
||||
|
||||
lookahead++;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// If Block -> Process Block
|
||||
if (BlockParser.IsBlock(CurrentLine))
|
||||
{
|
||||
try
|
||||
{
|
||||
var block = BlockParser.Parse(CurrentLine);
|
||||
CurrentBlock = block.Label;
|
||||
if (!block.Disabled)
|
||||
block.Process(data);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
data.LogBuffer.Add(new LogEntry("ERROR: " + ex.Message, Colors.Tomato));
|
||||
data.Status = BotStatus.ERROR;
|
||||
throw new BlockProcessingException(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
// If Command -> Process Command
|
||||
else if (CommandParser.IsCommand(CurrentLine))
|
||||
{
|
||||
try
|
||||
{
|
||||
var action = CommandParser.Parse(CurrentLine, data);
|
||||
action?.Invoke();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
data.LogBuffer.Add(new LogEntry("ERROR: " + ex.Message, Colors.Tomato));
|
||||
data.Status = BotStatus.ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
// Try to Process Flow Control
|
||||
else
|
||||
{
|
||||
var cfLine = CurrentLine;
|
||||
var token = LineParser.ParseToken(ref cfLine, TokenType.Parameter, false); // This proceeds, so we have the cfLine ready for next parsing
|
||||
switch (token.ToUpper())
|
||||
{
|
||||
case "IF":
|
||||
// Check condition, if not true jump to line after first ELSE or ENDIF (check both at the same time on lines, not separately)
|
||||
if (!ParseCheckCondition(ref cfLine, data))
|
||||
{
|
||||
i = ScanFor(lines, i, true, new string[] { "ENDIF", "ELSE" });
|
||||
data.LogBuffer.Add(new LogEntry($"Jumping to line {i + 1}", Colors.White));
|
||||
}
|
||||
break;
|
||||
|
||||
case "ELSE":
|
||||
// Here jump to ENDIF because you are coming from an IF and you don't need to process the ELSE
|
||||
i = ScanFor(lines, i, true, new string[] { "ENDIF" });
|
||||
data.LogBuffer.Add(new LogEntry($"Jumping to line {i + 1}", Colors.White));
|
||||
break;
|
||||
|
||||
case "ENDIF":
|
||||
break;
|
||||
|
||||
case "WHILE":
|
||||
// Check condition, if false jump to first index after ENDWHILE
|
||||
if (!ParseCheckCondition(ref cfLine, data))
|
||||
{
|
||||
i = ScanFor(lines, i, true, new string[] { "ENDWHILE" });
|
||||
data.LogBuffer.Add(new LogEntry($"Jumping to line {i + 1}", Colors.White));
|
||||
}
|
||||
break;
|
||||
|
||||
case "ENDWHILE":
|
||||
// Jump back to the previous WHILE index
|
||||
i = ScanFor(lines, i, false, new string[] { "WHILE" }) - 1;
|
||||
data.LogBuffer.Add(new LogEntry($"Jumping to line {i + 1}", Colors.White));
|
||||
break;
|
||||
|
||||
case "JUMP":
|
||||
var label = "";
|
||||
try
|
||||
{
|
||||
label = LineParser.ParseToken(ref cfLine, TokenType.Label, true);
|
||||
i = ScanFor(lines, 0, true, new string[] { $"{label}" }) - 1;
|
||||
data.LogBuffer.Add(new LogEntry($"Jumping to line {i + 1}", Colors.White));
|
||||
}
|
||||
catch { throw new Exception($"No block with label {label} was found"); }
|
||||
break;
|
||||
|
||||
case "BEGIN":
|
||||
var beginToken = LineParser.ParseToken(ref cfLine, TokenType.Parameter, true);
|
||||
switch (beginToken.ToUpper())
|
||||
{
|
||||
case "SCRIPT":
|
||||
language = (ScriptingLanguage)LineParser.ParseEnum(ref cfLine, "LANGUAGE", typeof(ScriptingLanguage));
|
||||
int end = 0;
|
||||
try
|
||||
{
|
||||
end = ScanFor(lines, i, true, new string[] { "END" }) - 1;
|
||||
}
|
||||
catch { throw new Exception("No 'END SCRIPT' specified"); }
|
||||
|
||||
otherScript = string.Join(Environment.NewLine, lines.Skip(i + 1).Take(end - i));
|
||||
i = end;
|
||||
data.LogBuffer.Add(new LogEntry($"Jumping to line {i + 1}", Colors.White));
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case "END":
|
||||
var endToken = LineParser.ParseToken(ref cfLine, TokenType.Parameter, true);
|
||||
switch (endToken.ToUpper())
|
||||
{
|
||||
case "SCRIPT":
|
||||
LineParser.EnsureIdentifier(ref cfLine, "->");
|
||||
LineParser.EnsureIdentifier(ref cfLine, "VARS");
|
||||
var outputs = LineParser.ParseLiteral(ref cfLine, "OUTPUTS");
|
||||
|
||||
try
|
||||
{
|
||||
if (otherScript != "") RunScript(otherScript, language, outputs, data);
|
||||
}
|
||||
catch (Exception ex) { data.LogBuffer.Add(new LogEntry($"The script failed to be executed: {ex.Message}", Colors.Tomato)); }
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (BlockProcessingException) { throw; } // Rethrow the Block Processing Exception so the error can be displayed in the view above
|
||||
catch (Exception e) { throw new Exception($"Parsing Exception on line {i + 1}: {e.Message}"); } // Catch inner and throw line exception
|
||||
|
||||
i += 1 + lookahead;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests if a line is empty, a comment (starting with ##) or if it's disabled (starting with !).
|
||||
/// </summary>
|
||||
/// <param name="line">The line to test.</param>
|
||||
/// <returns>Whether the line needs to be skipped</returns>
|
||||
private static bool IsEmptyOrCommentOrDisabled(string line)
|
||||
{
|
||||
try { return line.Trim() == "" || line.StartsWith("##") || line.StartsWith("!"); }
|
||||
catch { return true; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Scans for a given set of identifiers in the script and returns the line index value of the first found.
|
||||
/// </summary>
|
||||
/// <param name="lines">The lines of the script</param>
|
||||
/// <param name="current">The index of the current line</param>
|
||||
/// <param name="downwards">Whether to scan downwards or upwards</param>
|
||||
/// <param name="options">The target identifiers</param>
|
||||
/// <returns></returns>
|
||||
public static int ScanFor(string[] lines, int current, bool downwards, string[] options)
|
||||
{
|
||||
var i = downwards ? current + 1 : current - 1;
|
||||
var found = false;
|
||||
while (i >= 0 && i < lines.Count())
|
||||
{
|
||||
try
|
||||
{
|
||||
var token = LineParser.ParseToken(ref lines[i], TokenType.Parameter, false, false);
|
||||
if (options.Any(o => token.ToUpper() == o.ToUpper()))
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
|
||||
if (downwards) i++;
|
||||
else i--;
|
||||
}
|
||||
|
||||
if (found) return i;
|
||||
else throw new Exception("Not found");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses a condition made of left-hand term, condition type and right-hand term and verifies if it's true.
|
||||
/// </summary>
|
||||
/// <param name="cfLine">The reference to the line to parse</param>
|
||||
/// <param name="data">The BotData needed for variable replacement</param>
|
||||
/// <returns></returns>
|
||||
public static bool ParseCheckCondition(ref string cfLine, BotData data)
|
||||
{
|
||||
var first = LineParser.ParseLiteral(ref cfLine, "STRING");
|
||||
var condition = (Condition)LineParser.ParseEnum(ref cfLine, "CONDITION", typeof(Condition));
|
||||
var second = "";
|
||||
if (condition != Condition.Exists)
|
||||
second = LineParser.ParseLiteral(ref cfLine, "STRING");
|
||||
return (ConditionChecker.Verify(first, condition, second, data));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs a script with a different language inside the LoliScript.
|
||||
/// </summary>
|
||||
/// <param name="script">The script as a string with linebreaks</param>
|
||||
/// <param name="language">The language of the script</param>
|
||||
/// <param name="outputs">The variables that should be extracted from the script's scope and set into the BotData local variables</param>
|
||||
/// <param name="data">The BotData needed for variable replacement</param>
|
||||
private void RunScript(string script, ScriptingLanguage language, string outputs, BotData data)
|
||||
{
|
||||
// Set the console output to stringwriter
|
||||
var sw = new StringWriter();
|
||||
Console.SetOut(sw);
|
||||
Console.SetError(sw);
|
||||
|
||||
// Parse variables to get out
|
||||
List<string> outVarList = new List<string>();
|
||||
if (outputs != "")
|
||||
{
|
||||
try { outVarList = outputs.Split(',').Select(x => x.Trim()).ToList(); }
|
||||
catch { }
|
||||
}
|
||||
|
||||
var start = DateTime.Now;
|
||||
|
||||
try
|
||||
{
|
||||
switch (language)
|
||||
{
|
||||
case ScriptingLanguage.JavaScript:
|
||||
|
||||
// Redefine log() function
|
||||
var jsengine = new Engine().SetValue("log", new Action<object>(Console.WriteLine));
|
||||
|
||||
// Add in all the variables
|
||||
foreach (var variable in data.Variables.All)
|
||||
{
|
||||
try { jsengine.SetValue(variable.Name, variable.Value); } catch { }
|
||||
}
|
||||
|
||||
// Execute JS
|
||||
jsengine.Execute(script);
|
||||
|
||||
// Print results to log
|
||||
data.Log(new LogEntry($"DEBUG LOG: {sw.ToString()}", Colors.White));
|
||||
|
||||
// Get variables out
|
||||
data.Log(new LogEntry($"Parsing {outVarList.Count} variables", Colors.White));
|
||||
foreach (var name in outVarList)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Add it to the variables and print info
|
||||
var value = jsengine.Global.GetProperty(name).Value;
|
||||
var isArray = value.IsArray();
|
||||
if (isArray) data.Variables.Set(new CVar(name, CVar.VarType.List, value.TryCast<List<string>>()));
|
||||
else data.Variables.Set(new CVar(name, CVar.VarType.Single, value.ToString()));
|
||||
data.Log(new LogEntry($"SET VARIABLE {name} WITH VALUE {value.ToString()}", Colors.Yellow));
|
||||
}
|
||||
catch { data.Log(new LogEntry($"COULD NOT FIND VARIABLE {name}", Colors.Tomato)); }
|
||||
}
|
||||
|
||||
// Print other info
|
||||
if (jsengine.GetCompletionValue() != null)
|
||||
data.Log(new LogEntry($"Completion value: {jsengine.GetCompletionValue()}", Colors.White));
|
||||
|
||||
break;
|
||||
|
||||
case ScriptingLanguage.IronPython:
|
||||
|
||||
// Initialize the engine
|
||||
var runtime = Python.CreateRuntime();
|
||||
var pyengine = runtime.GetEngine("py");
|
||||
PythonCompilerOptions pco = (PythonCompilerOptions)pyengine.GetCompilerOptions();
|
||||
pco.Module &= ~ModuleOptions.Optimized;
|
||||
//var pyengine = Python.CreateEngine();
|
||||
var scope = pyengine.CreateScope();
|
||||
var code = pyengine.CreateScriptSourceFromString(script);
|
||||
|
||||
// Add in all the variables
|
||||
foreach (var variable in data.Variables.All)
|
||||
try { scope.SetVariable(variable.Name, variable.Value); } catch { }
|
||||
|
||||
// Execute it
|
||||
var result = code.Execute(scope);
|
||||
//var result = pyengine.Execute(script, scope);
|
||||
|
||||
// Print the logs
|
||||
data.Log(new LogEntry($"DEBUG LOG: {sw.ToString()}", Colors.White));
|
||||
|
||||
// Get variables out
|
||||
data.Log(new LogEntry($"Parsing {outVarList.Count} variables", Colors.White));
|
||||
foreach (var name in outVarList)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Add it to the variables and print info
|
||||
var value = scope.GetVariable(name);
|
||||
data.Variables.Set(new CVar(name, (value.GetType() == typeof(string[])) ? CVar.VarType.List : CVar.VarType.Single, value));
|
||||
data.Log(new LogEntry($"SET VARIABLE {name} WITH VALUE {value}", Colors.Yellow));
|
||||
}
|
||||
catch { data.Log(new LogEntry($"COULD NOT FIND VARIABLE {name}", Colors.Tomato)); }
|
||||
}
|
||||
|
||||
// Print other info
|
||||
if (result != null)
|
||||
data.Log(new LogEntry($"Completion value: {result}", Colors.White));
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
data.Log(new LogEntry($"Execution completed in {(DateTime.Now - start).TotalSeconds} seconds", Colors.GreenYellow));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
data.Log(new LogEntry($"[ERROR] INFO: {e.Message}", Colors.White));
|
||||
}
|
||||
finally
|
||||
{
|
||||
var standardOutput = new StreamWriter(Console.OpenStandardOutput());
|
||||
var standardError = new StreamWriter(Console.OpenStandardError());
|
||||
standardOutput.AutoFlush = true;
|
||||
standardError.AutoFlush = true;
|
||||
Console.SetOut(standardOutput);
|
||||
Console.SetError(standardError);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace RuriLib.LS
|
||||
{
|
||||
/// <summary>
|
||||
/// Parses a block from LoliScript code.
|
||||
/// </summary>
|
||||
public static class BlockParser
|
||||
{
|
||||
/// <summary>
|
||||
/// The allowed block identifiers.
|
||||
/// </summary>
|
||||
public enum BlockName
|
||||
{
|
||||
/// <summary>The BYPASSCF block.</summary>
|
||||
BYPASSCF,
|
||||
|
||||
/// <summary>The CAPTCHA block.</summary>
|
||||
CAPTCHA,
|
||||
|
||||
/// <summary>The FUNCTION block.</summary>
|
||||
FUNCTION,
|
||||
|
||||
/// <summary>The KEYCHECK block.</summary>
|
||||
KEYCHECK,
|
||||
|
||||
/// <summary>The PARSE block.</summary>
|
||||
PARSE,
|
||||
|
||||
/// <summary>The RECAPTCHA block.</summary>
|
||||
RECAPTCHA,
|
||||
|
||||
/// <summary>The REQUEST block.</summary>
|
||||
REQUEST,
|
||||
|
||||
/// <summary>The TCP block.</summary>
|
||||
TCP,
|
||||
|
||||
/// <summary>The UTILITY block.</summary>
|
||||
UTILITY,
|
||||
|
||||
/// <summary>The BROWSERACTION block.</summary>
|
||||
BROWSERACTION,
|
||||
|
||||
/// <summary>The ELEMENTACTION block.</summary>
|
||||
ELEMENTACTION,
|
||||
|
||||
/// <summary>The EXECUTEJS block.</summary>
|
||||
EXECUTEJS,
|
||||
|
||||
/// <summary>The NAVIGATE block.</summary>
|
||||
NAVIGATE
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests if a line is parsable as a block.
|
||||
/// </summary>
|
||||
/// <param name="line">The data line to test</param>
|
||||
/// <returns>Whether the line contains a block or not.</returns>
|
||||
public static bool IsBlock(string line)
|
||||
{
|
||||
return Enum.GetNames(typeof(BlockName))
|
||||
.Select(n => n.ToUpper())
|
||||
.Contains(GetBlockType(line).ToUpper());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the block type from a block line.
|
||||
/// </summary>
|
||||
/// <param name="line">The block line</param>
|
||||
/// <returns>The type of the block</returns>
|
||||
public static string GetBlockType(string line)
|
||||
{
|
||||
var groups = Regex.Match(line, @"^!?(#[^ ]* )?([^ ]*)").Groups;
|
||||
return groups[2].Value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses a block line as a block object.
|
||||
/// </summary>
|
||||
/// <param name="line">The block line</param>
|
||||
/// <returns>The parsed block object</returns>
|
||||
public static BlockBase Parse(string line)
|
||||
{
|
||||
// Trim the line
|
||||
var input = line.Trim();
|
||||
|
||||
// Return an exception if the line is empty
|
||||
if (input == "") throw new ArgumentNullException();
|
||||
|
||||
// Parse if disabled or not
|
||||
var disabled = input.StartsWith("!");
|
||||
if (disabled) input = input.Substring(1).Trim();
|
||||
|
||||
var label = LineParser.ParseToken(ref input, TokenType.Label, false);
|
||||
|
||||
// Parse the identifier
|
||||
var identifier = "";
|
||||
try { identifier = LineParser.ParseToken(ref input, TokenType.Parameter, true); }
|
||||
catch { throw new ArgumentException("Missing identifier"); }
|
||||
|
||||
BlockBase block = null;
|
||||
switch ((BlockName)Enum.Parse(typeof(BlockName), identifier, true))
|
||||
{
|
||||
case BlockName.FUNCTION:
|
||||
block = (new BlockFunction()).FromLS(input);
|
||||
break;
|
||||
|
||||
case BlockName.KEYCHECK:
|
||||
block = (new BlockKeycheck()).FromLS(input);
|
||||
break;
|
||||
|
||||
case BlockName.RECAPTCHA:
|
||||
block = (new BlockRecaptcha()).FromLS(input);
|
||||
break;
|
||||
|
||||
case BlockName.REQUEST:
|
||||
block = (new BlockRequest()).FromLS(input);
|
||||
break;
|
||||
|
||||
case BlockName.PARSE:
|
||||
block = (new BlockParse()).FromLS(input);
|
||||
break;
|
||||
|
||||
case BlockName.CAPTCHA:
|
||||
block = (new BlockImageCaptcha()).FromLS(input);
|
||||
break;
|
||||
|
||||
case BlockName.BYPASSCF:
|
||||
block = (new BlockBypassCF()).FromLS(input);
|
||||
break;
|
||||
|
||||
case BlockName.UTILITY:
|
||||
block = (new BlockUtility()).FromLS(input);
|
||||
break;
|
||||
|
||||
case BlockName.TCP:
|
||||
block = (new BlockTCP()).FromLS(input);
|
||||
break;
|
||||
|
||||
case BlockName.NAVIGATE:
|
||||
block = (new SBlockNavigate()).FromLS(input);
|
||||
break;
|
||||
|
||||
case BlockName.BROWSERACTION:
|
||||
block = (new SBlockBrowserAction()).FromLS(input);
|
||||
break;
|
||||
|
||||
case BlockName.ELEMENTACTION:
|
||||
block = (new SBlockElementAction()).FromLS(input);
|
||||
break;
|
||||
|
||||
case BlockName.EXECUTEJS:
|
||||
block = (new SBlockExecuteJS()).FromLS(input);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new ArgumentException($"Invalid identifier '{identifier}'");
|
||||
}
|
||||
|
||||
// Set disabled
|
||||
if (block != null) block.Disabled = disabled;
|
||||
|
||||
// Set the label
|
||||
if (block != null && label != "") block.Label = label.Replace("#", "");
|
||||
|
||||
return block;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace RuriLib.LS
|
||||
{
|
||||
/// <summary>
|
||||
/// Parse a command from LoliScript code.
|
||||
/// </summary>
|
||||
public class CommandParser
|
||||
{
|
||||
/// <summary>
|
||||
/// The allowed command identifiers.
|
||||
/// </summary>
|
||||
public enum CommandName
|
||||
{
|
||||
/// <summary>Prints some data to the log after replacing variables in it.</summary>
|
||||
PRINT,
|
||||
|
||||
/// <summary>Sets the value of hidden variables.</summary>
|
||||
SET,
|
||||
|
||||
/// <summary>Deletes variables.</summary>
|
||||
DELETE,
|
||||
|
||||
/// <summary>Moves the mouse in a selenium-driven browser.</summary>
|
||||
MOUSEACTION
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests if a line is parsable as a command.
|
||||
/// </summary>
|
||||
/// <param name="line">The data line to test</param>
|
||||
/// <returns>Whether the line contains a command or not</returns>
|
||||
public static bool IsCommand(string line)
|
||||
{
|
||||
var groups = Regex.Match(line, @"^([^ ]*)").Groups;
|
||||
return Enum.GetNames(typeof(CommandName)).Select(n => n.ToUpper()).Contains(groups[1].Value.ToUpper());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a command Action from a command line.
|
||||
/// </summary>
|
||||
/// <param name="line">The command line</param>
|
||||
/// <param name="data">The BotData needed for variable replacement</param>
|
||||
/// <returns>The Action that needs to be executed</returns>
|
||||
public static Action Parse(string line, BotData data)
|
||||
{
|
||||
// Trim the line
|
||||
var input = line.Trim();
|
||||
|
||||
// Return an exception if the line is empty
|
||||
if (input == "") throw new ArgumentNullException();
|
||||
|
||||
var label = LineParser.ParseToken(ref input, TokenType.Label, false);
|
||||
|
||||
// Parse the identifier
|
||||
var identifier = "";
|
||||
try { identifier = LineParser.ParseToken(ref input, TokenType.Parameter, true); }
|
||||
catch { throw new ArgumentException("Missing identifier"); }
|
||||
|
||||
switch ((CommandName)Enum.Parse(typeof(CommandName), identifier, true))
|
||||
{
|
||||
case CommandName.PRINT:
|
||||
return new Action(() => data.Log(new LogEntry(BlockBase.ReplaceValues(input, data), Colors.White)));
|
||||
|
||||
case CommandName.SET:
|
||||
return SetParser.Parse(input, data);
|
||||
|
||||
case CommandName.DELETE:
|
||||
return DeleteParser.Parse(input, data);
|
||||
|
||||
case CommandName.MOUSEACTION:
|
||||
return MouseActionParser.Parse(input, data);
|
||||
|
||||
default:
|
||||
throw new ArgumentException($"Invalid identifier '{identifier}'");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace RuriLib.LS
|
||||
{
|
||||
/// <summary>
|
||||
/// Parses a DELETE command.
|
||||
/// </summary>
|
||||
class DeleteParser
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the Action that needs to be executed.
|
||||
/// </summary>
|
||||
/// <param name="line">The data line to parse</param>
|
||||
/// <param name="data">The BotData needed for variable replacement</param>
|
||||
/// <returns>The Action to execute</returns>
|
||||
public static Action Parse(string line, BotData data)
|
||||
{
|
||||
var input = line.Trim();
|
||||
var field = LineParser.ParseToken(ref input, TokenType.Parameter, true).ToUpper();
|
||||
|
||||
return new Action(() =>
|
||||
{
|
||||
var name = "";
|
||||
Condition cond = Condition.EqualTo;
|
||||
|
||||
switch (field)
|
||||
{
|
||||
case "COOKIE":
|
||||
if(LineParser.Lookahead(ref input) == TokenType.Parameter)
|
||||
cond = (Condition)LineParser.ParseEnum(ref input, "TYPE", typeof(Condition));
|
||||
name = LineParser.ParseLiteral(ref input, "NAME");
|
||||
for (int i=0; i<data.Cookies.Count; i++)
|
||||
{
|
||||
var curr = data.Cookies.ToList()[i].Key;
|
||||
if (ConditionChecker.Verify(curr, cond, name, data)) data.Cookies.Remove(curr);
|
||||
}
|
||||
break;
|
||||
|
||||
case "VAR":
|
||||
if (LineParser.Lookahead(ref input) == TokenType.Parameter)
|
||||
cond = (Condition)LineParser.ParseEnum(ref input, "TYPE", typeof(Condition));
|
||||
name = LineParser.ParseLiteral(ref input, "NAME");
|
||||
data.Variables.Remove(cond, name, data);
|
||||
break;
|
||||
|
||||
case "GVAR":
|
||||
if (LineParser.Lookahead(ref input) == TokenType.Parameter)
|
||||
cond = (Condition)LineParser.ParseEnum(ref input, "TYPE", typeof(Condition));
|
||||
name = LineParser.ParseLiteral(ref input, "NAME");
|
||||
try
|
||||
{
|
||||
data.GlobalVariables.Remove(cond, name, data);
|
||||
}
|
||||
catch { }
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new ArgumentException($"Invalid identifier {field}");
|
||||
}
|
||||
|
||||
data.Log(new LogEntry($"DELETE command executed on field {field}", Colors.White));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,232 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace RuriLib.LS
|
||||
{
|
||||
/// <summary>
|
||||
/// The allowed types of tokens that can be parsed from a line.
|
||||
/// </summary>
|
||||
public enum TokenType
|
||||
{
|
||||
/// <summary>A block label.</summary>
|
||||
Label,
|
||||
|
||||
/// <summary>A generic parameter, usually an enum value.</summary>
|
||||
Parameter,
|
||||
|
||||
/// <summary>A string between double quotes.</summary>
|
||||
Literal,
|
||||
|
||||
/// <summary>The character sequence ->.</summary>
|
||||
Arrow,
|
||||
|
||||
/// <summary>A boolean value in the format Name=Value where Name is the name of the property.</summary>
|
||||
Boolean,
|
||||
|
||||
/// <summary>An integer value.</summary>
|
||||
Integer
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Contains methods used to parse tokens from a LoliScript line of code.
|
||||
/// </summary>
|
||||
public static class LineParser
|
||||
{
|
||||
/// <summary>
|
||||
/// Parses a token of a given type from a line.
|
||||
/// </summary>
|
||||
/// <param name="input">The reference to the line of code</param>
|
||||
/// <param name="type">The type of token to parse</param>
|
||||
/// <param name="essential">Whether an exception should be thrown if the token cannot be parsed</param>
|
||||
/// <param name="proceed">Whether to remove the token from the original line after parsing it</param>
|
||||
/// <returns>The parsed token or, if parse fails but the token is not essential, an empty string</returns>
|
||||
public static string ParseToken(ref string input, TokenType type, bool essential, bool proceed = true)
|
||||
{
|
||||
var pattern = GetPattern(type);
|
||||
var token = "";
|
||||
|
||||
Regex r = new Regex(pattern);
|
||||
Match m = r.Match(input);
|
||||
if (m.Success)
|
||||
{
|
||||
token = m.Value;
|
||||
|
||||
if (proceed)
|
||||
input = input.Substring(token.Length).Trim();
|
||||
|
||||
if (type == TokenType.Literal)
|
||||
token = token.Substring(1, token.Length - 2).Replace("\\\"", "\"");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (essential)
|
||||
{
|
||||
throw new ArgumentException("Cannot parse token");
|
||||
}
|
||||
}
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a boolean property by parsing its name and value from a line.
|
||||
/// </summary>
|
||||
/// <param name="input">The reference to the line of code</param>
|
||||
/// <param name="instance">The instance of the object containing the boolean property</param>
|
||||
public static void SetBool(ref string input, object instance)
|
||||
{
|
||||
var result = ParseToken(ref input, TokenType.Parameter, true, true).Split('=');
|
||||
PropertyInfo prop = null;
|
||||
try
|
||||
{
|
||||
prop = instance.GetType().GetProperty(result[0]);
|
||||
}
|
||||
catch { throw new ArgumentException($"There is no property called {result[0]} in the type {instance.GetType().ToString()}"); }
|
||||
var propVal = prop.GetValue(instance);
|
||||
if (propVal.GetType() != typeof(bool)) throw new InvalidCastException($"The property {result[0]} is not a boolean");
|
||||
|
||||
switch (result[1].ToUpper())
|
||||
{
|
||||
case "TRUE":
|
||||
prop.SetValue(instance, true);
|
||||
break;
|
||||
|
||||
case "FALSE":
|
||||
prop.SetValue(instance, false);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new ArgumentException($"Expected bool value for '{prop.Name}'");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses an enum value from a line.
|
||||
/// </summary>
|
||||
/// <param name="input">The reference to the line of code</param>
|
||||
/// <param name="label">Debug information about the expected enum</param>
|
||||
/// <param name="enumType">The type of the enum</param>
|
||||
/// <returns>An enum of the provided enumType</returns>
|
||||
public static dynamic ParseEnum(ref string input, string label, Type enumType)
|
||||
{
|
||||
// Parse the token first
|
||||
var token = "";
|
||||
try { token = ParseToken(ref input, TokenType.Parameter, true); }
|
||||
catch { throw new ArgumentException($"Missing '{label}'"); }
|
||||
try { return Enum.Parse(enumType, token, true); }
|
||||
catch { throw new ArgumentException($"Invalid '{label}'"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses a literal value from a line.
|
||||
/// </summary>
|
||||
/// <param name="input">The reference to the line of code</param>
|
||||
/// <param name="label">Debug information about the expected literal</param>
|
||||
/// <param name="replace">Whether to perform variable replacement in the literal</param>
|
||||
/// <param name="data">The BotData needed for variable replacement</param>
|
||||
/// <returns>The literal without the leading and trailing double quotes</returns>
|
||||
public static string ParseLiteral(ref string input, string label, bool replace = false, BotData data = null)
|
||||
{
|
||||
try {
|
||||
if (replace) return BlockBase.ReplaceValues(ParseToken(ref input, TokenType.Literal, true), data);
|
||||
else return ParseToken(ref input, TokenType.Literal, true);
|
||||
}
|
||||
catch { throw new ArgumentException($"Expected Literal value for '{label}'"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses an integer value from a line.
|
||||
/// </summary>
|
||||
/// <param name="input">The reference to the line of code</param>
|
||||
/// <param name="label">Debug information about the expected integer</param>
|
||||
/// <returns>The integer value</returns>
|
||||
public static int ParseInt(ref string input, string label)
|
||||
{
|
||||
try { return int.Parse(LineParser.ParseToken(ref input, TokenType.Parameter, true)); }
|
||||
catch { throw new ArgumentException($"Expected Integer value for '{label}'"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses a block label from a line.
|
||||
/// </summary>
|
||||
/// <param name="input">The reference to the line of code</param>
|
||||
/// <returns>The label of the block, if defined</returns>
|
||||
public static string ParseLabel(ref string input)
|
||||
{
|
||||
return ParseToken(ref input, TokenType.Label, false).Substring(1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Makes sure that a specified identifier is present and moves past it. An exception will be thrown if the identifier is not present.
|
||||
/// </summary>
|
||||
/// <param name="input">The reference to the line of code</param>
|
||||
/// <param name="id">The expected identifier</param>
|
||||
public static void EnsureIdentifier(ref string input, string id)
|
||||
{
|
||||
var token = ParseToken(ref input, TokenType.Parameter, true);
|
||||
if (token.ToUpper() != id.ToUpper())
|
||||
throw new ArgumentException($"Expected identifier '{id}'");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of the first token in the given line of code.
|
||||
/// </summary>
|
||||
/// <param name="input">The reference to the line of code</param>
|
||||
/// <returns>The type of the token</returns>
|
||||
public static TokenType Lookahead(ref string input)
|
||||
{
|
||||
var i = 0;
|
||||
var token = ParseToken(ref input, TokenType.Parameter, true, false);
|
||||
if (token.Contains("\"")) return TokenType.Literal;
|
||||
else if (token == "->") return TokenType.Arrow;
|
||||
else if (token.StartsWith("#")) return TokenType.Label;
|
||||
else if (token.ToUpper().Contains("=TRUE") || token.ToUpper().Contains("=FALSE")) return TokenType.Boolean;
|
||||
else if (int.TryParse(token, out i)) return TokenType.Integer;
|
||||
else return TokenType.Parameter;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the next token is a given identifier.
|
||||
/// </summary>
|
||||
/// <param name="input">The reference to the line of code</param>
|
||||
/// <param name="id">The identifier to check</param>
|
||||
/// <returns>Whether the token and the identifier are equal</returns>
|
||||
public static bool CheckIdentifier(ref string input, string id)
|
||||
{
|
||||
try
|
||||
{
|
||||
var token = ParseToken(ref input, TokenType.Parameter, true, false);
|
||||
return token.ToUpper() == id.ToUpper();
|
||||
}
|
||||
catch { return false; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the regex pattern to match a given token type.
|
||||
/// </summary>
|
||||
/// <param name="type">The token type</param>
|
||||
/// <returns>The regex pattern to parse the token type</returns>
|
||||
private static string GetPattern(TokenType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case TokenType.Label:
|
||||
return "^#[^ ]*";
|
||||
|
||||
case TokenType.Parameter:
|
||||
return "^[^ ]*";
|
||||
|
||||
case TokenType.Literal:
|
||||
return "\\\"(\\\\.|[^\\\"])*\\\"";
|
||||
|
||||
case TokenType.Arrow:
|
||||
return "^->";
|
||||
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,596 @@
|
||||
using OpenQA.Selenium;
|
||||
using OpenQA.Selenium.Interactions;
|
||||
using OpenQA.Selenium.Remote;
|
||||
using RuriLib.LS;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace RuriLib.LS
|
||||
{
|
||||
/// <summary>
|
||||
/// Parses a MOUSEACTION command.
|
||||
/// </summary>
|
||||
class MouseActionParser
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the Action that needs to be executed.
|
||||
/// </summary>
|
||||
/// <param name="line">The data line to parse</param>
|
||||
/// <param name="data">The BotData needed for variable replacement</param>
|
||||
/// <returns>The Action to execute</returns>
|
||||
public static Action Parse(string line, BotData data)
|
||||
{
|
||||
// Trim the line
|
||||
var input = line.Trim();
|
||||
|
||||
// Initialize the action chain
|
||||
Actions actions = null;
|
||||
try
|
||||
{
|
||||
actions = new Actions(data.Driver);
|
||||
}
|
||||
catch { throw new Exception("No Browser initialized!"); }
|
||||
|
||||
// Build it
|
||||
var offsetX = 0;
|
||||
var offsetY = 0;
|
||||
var point1X = 0;
|
||||
var point1Y = 0;
|
||||
var point2X = 0;
|
||||
var point2Y = 0;
|
||||
var key = "";
|
||||
var gravity = 1;
|
||||
var wind = 1;
|
||||
var qty = 0;
|
||||
IWebElement elem1 = null;
|
||||
IWebElement elem2 = null;
|
||||
Line newLine = null;
|
||||
while (input != "")
|
||||
{
|
||||
var parsed = LineParser.ParseToken(ref input, TokenType.Parameter, true).ToUpper();
|
||||
switch (parsed)
|
||||
{
|
||||
case "SPAWN":
|
||||
// Spawn a div in a certain position so you can hook to it later via id
|
||||
SpawnDiv(data.Driver, LineParser.ParseInt(ref input, "X"), LineParser.ParseInt(ref input, "Y"), LineParser.ParseLiteral(ref input, "ID"));
|
||||
break;
|
||||
|
||||
case "CLICK":
|
||||
if (!LineParser.CheckIdentifier(ref input, "ELEMENT"))
|
||||
actions.Click();
|
||||
else
|
||||
actions.Click(ParseElement(ref input, data));
|
||||
break;
|
||||
|
||||
case "CLICKANDHOLD":
|
||||
if (!LineParser.CheckIdentifier(ref input, "ELEMENT"))
|
||||
actions.ClickAndHold();
|
||||
else
|
||||
actions.ClickAndHold(ParseElement(ref input, data));
|
||||
break;
|
||||
|
||||
case "RIGHTCLICK":
|
||||
if (!LineParser.CheckIdentifier(ref input, "ELEMENT"))
|
||||
actions.ContextClick();
|
||||
else
|
||||
actions.ContextClick(ParseElement(ref input, data));
|
||||
break;
|
||||
|
||||
case "DOUBLECLICK":
|
||||
if (!LineParser.CheckIdentifier(ref input, "ELEMENT"))
|
||||
actions.DoubleClick();
|
||||
else
|
||||
actions.DoubleClick(ParseElement(ref input, data));
|
||||
break;
|
||||
|
||||
case "DRAGANDDROP":
|
||||
elem1 = ParseElement(ref input, data);
|
||||
LineParser.ParseToken(ref input, TokenType.Arrow, true);
|
||||
elem2 = ParseElement(ref input, data);
|
||||
actions.DragAndDrop(elem1, elem2);
|
||||
break;
|
||||
|
||||
case "DRAGANDDROPWITHOFFSET":
|
||||
offsetX = LineParser.ParseInt(ref input, "OFFSET X");
|
||||
offsetY = LineParser.ParseInt(ref input, "OFFSET Y");
|
||||
actions.DragAndDropToOffset(ParseElement(ref input, data), offsetX, offsetY);
|
||||
break;
|
||||
|
||||
case "KEYDOWN":
|
||||
key = LineParser.ParseLiteral(ref input, "KEY", true, data);
|
||||
if (!LineParser.CheckIdentifier(ref input, "ELEMENT"))
|
||||
actions.KeyDown(key);
|
||||
else
|
||||
actions.KeyDown(ParseElement(ref input, data), key);
|
||||
break;
|
||||
|
||||
case "KEYUP":
|
||||
key = LineParser.ParseLiteral(ref input, "KEY", true, data);
|
||||
if (!LineParser.CheckIdentifier(ref input, "ELEMENT"))
|
||||
actions.KeyUp(key);
|
||||
else
|
||||
actions.KeyUp(ParseElement(ref input, data), key);
|
||||
break;
|
||||
|
||||
case "MOVEBY":
|
||||
offsetX = LineParser.ParseInt(ref input, "OFFSET X");
|
||||
offsetY = LineParser.ParseInt(ref input, "OFFSET Y");
|
||||
actions.MoveByOffset(offsetX, offsetY);
|
||||
break;
|
||||
|
||||
case "MOVETO":
|
||||
actions.MoveToElement(ParseElement(ref input, data));
|
||||
break;
|
||||
|
||||
case "RELEASE":
|
||||
if (!LineParser.CheckIdentifier(ref input, "ELEMENT"))
|
||||
actions.Release();
|
||||
else
|
||||
actions.Release(ParseElement(ref input, data));
|
||||
break;
|
||||
|
||||
case "SENDKEYS":
|
||||
key = LineParser.ParseLiteral(ref input, "KEY", true, data);
|
||||
if (!LineParser.CheckIdentifier(ref input, "ELEMENT"))
|
||||
actions.SendKeys(key);
|
||||
else
|
||||
actions.SendKeys(ParseElement(ref input, data), key);
|
||||
break;
|
||||
|
||||
case "DRAWPOINTS":
|
||||
offsetX = LineParser.ParseInt(ref input, "MAX WIDTH");
|
||||
offsetY = LineParser.ParseInt(ref input, "MAX HEIGHT");
|
||||
var amount = LineParser.ParseInt(ref input, "AMOUNT");
|
||||
Random rand = new Random();
|
||||
var previousx = 0;
|
||||
var previousy = 0;
|
||||
// Move to the first point
|
||||
actions.MoveToElement(data.Driver.FindElementByTagName("body"), point1X, point1Y);
|
||||
List<Point> points = new List<Point>();
|
||||
for (int i = 0; i < amount; i++)
|
||||
{
|
||||
var x = rand.Next(0, offsetX);
|
||||
var y = rand.Next(0, offsetY);
|
||||
actions.MoveByOffset(x - previousx, y - previousy);
|
||||
previousx = x;
|
||||
previousy = y;
|
||||
points.Add(new Point(x, y));
|
||||
}
|
||||
if (data.GlobalSettings.Selenium.DrawMouseMovement)
|
||||
DrawRedDots(data.Driver, points.ToArray(), 5);
|
||||
break;
|
||||
|
||||
case "DRAWLINE":
|
||||
if (LineParser.Lookahead(ref input) == TokenType.Integer)
|
||||
{
|
||||
point1X = LineParser.ParseInt(ref input, "X1");
|
||||
point1Y = LineParser.ParseInt(ref input, "Y1");
|
||||
LineParser.ParseToken(ref input, TokenType.Arrow, true);
|
||||
point2X = LineParser.ParseInt(ref input, "X2");
|
||||
point2Y = LineParser.ParseInt(ref input, "Y2");
|
||||
}
|
||||
else
|
||||
{
|
||||
elem1 = ParseElement(ref input, data);
|
||||
point1X = elem1.Location.X;
|
||||
point1Y = elem1.Location.Y;
|
||||
LineParser.ParseToken(ref input, TokenType.Arrow, true);
|
||||
elem2 = ParseElement(ref input, data);
|
||||
point2X = elem2.Location.X;
|
||||
point2Y = elem2.Location.Y;
|
||||
}
|
||||
LineParser.EnsureIdentifier(ref input, ":");
|
||||
qty = LineParser.ParseInt(ref input, "QUANTITY");
|
||||
// Move to the first point
|
||||
actions.MoveToElement(data.Driver.FindElementByTagName("body"), point1X, point1Y);
|
||||
newLine = new Line(new Point(point1X, point1Y), new Point(point2X, point2Y));
|
||||
if (data.GlobalSettings.Selenium.DrawMouseMovement)
|
||||
DrawRedDots(data.Driver, newLine.getPoints(qty), 5);
|
||||
foreach (var p in newLine.getOffsets(qty))
|
||||
{
|
||||
actions.MoveByOffset(p.X, p.Y);
|
||||
}
|
||||
break;
|
||||
|
||||
case "DRAWLINEHUMAN":
|
||||
if (LineParser.Lookahead(ref input) == TokenType.Integer)
|
||||
{
|
||||
point1X = LineParser.ParseInt(ref input, "X1");
|
||||
point1Y = LineParser.ParseInt(ref input, "Y1");
|
||||
LineParser.ParseToken(ref input, TokenType.Arrow, true);
|
||||
point2X = LineParser.ParseInt(ref input, "X2");
|
||||
point2Y = LineParser.ParseInt(ref input, "Y2");
|
||||
}
|
||||
else
|
||||
{
|
||||
elem1 = ParseElement(ref input, data);
|
||||
point1X = elem1.Location.X;
|
||||
point1Y = elem1.Location.Y;
|
||||
LineParser.ParseToken(ref input, TokenType.Arrow, true);
|
||||
elem2 = ParseElement(ref input, data);
|
||||
point2X = elem2.Location.X;
|
||||
point2Y = elem2.Location.Y;
|
||||
}
|
||||
LineParser.EnsureIdentifier(ref input, ":");
|
||||
qty = LineParser.ParseInt(ref input, "QUANTITY");
|
||||
if (LineParser.Lookahead(ref input) == TokenType.Integer)
|
||||
{
|
||||
gravity = LineParser.ParseInt(ref input, "GRAVITY");
|
||||
wind = LineParser.ParseInt(ref input, "WIND");
|
||||
}
|
||||
// Move to the first point
|
||||
actions.MoveToElement(data.Driver.FindElementByTagName("body"), point1X, point1Y);
|
||||
newLine = new Line(new Point(point1X, point1Y), new Point(point2X, point2Y));
|
||||
var array = newLine.HumanWindMouse(point1X, point1Y, point2X, point2Y, gravity, wind, 1);
|
||||
var shrinked = ShrinkArray(array, qty);
|
||||
if (data.GlobalSettings.Selenium.DrawMouseMovement)
|
||||
DrawRedDots(data.Driver, shrinked, 5);
|
||||
foreach (var p in GetOffsets(shrinked))
|
||||
{
|
||||
actions.MoveByOffset(p.X, p.Y);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return new Action(() =>
|
||||
{
|
||||
actions.Build();
|
||||
actions.Perform();
|
||||
data.Log(new LogEntry("Executed Mouse Actions", Colors.White));
|
||||
});
|
||||
}
|
||||
|
||||
#region Action Creators
|
||||
/// <summary>
|
||||
/// Draws a red dot at the specified coordinates.
|
||||
/// </summary>
|
||||
/// <param name="driver">The selenium driver</param>
|
||||
/// <param name="x">The x coordinate of the dot</param>
|
||||
/// <param name="y">The y coordinate of the dot</param>
|
||||
/// <param name="thickness">The thickness in pixels of the dot</param>
|
||||
private static void DrawRedDot(RemoteWebDriver driver, int x, int y, int thickness)
|
||||
{
|
||||
try
|
||||
{
|
||||
var executor = driver as IJavaScriptExecutor;
|
||||
executor.ExecuteScript(RedDotScript(x, y, thickness));
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shrinks an array of points by removing random elements to fit a target size.
|
||||
/// </summary>
|
||||
/// <param name="originArray">The original array of points</param>
|
||||
/// <param name="targetSize">The target size of the array being returned</param>
|
||||
/// <returns>An array of points with the target size</returns>
|
||||
public static Point[] ShrinkArray(Point[] originArray, int targetSize)
|
||||
{
|
||||
Random rand = new Random();
|
||||
List<Point> origin = originArray.ToList();
|
||||
while (origin.Count > targetSize)
|
||||
{
|
||||
origin.RemoveAt(rand.Next(1, origin.Count - 2));
|
||||
}
|
||||
return origin.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Given an array of points, gets the offsets between one point and the next.
|
||||
/// </summary>
|
||||
/// <param name="originArray">The original array of points</param>
|
||||
/// <returns>The array of offsets</returns>
|
||||
private static Point[] GetOffsets(Point[] originArray)
|
||||
{
|
||||
List<Point> points = new List<Point>();
|
||||
var prev = originArray[0];
|
||||
foreach (var point in originArray)
|
||||
{
|
||||
points.Add(new Point(point.X - prev.X, point.Y - prev.Y));
|
||||
prev = point;
|
||||
}
|
||||
return points.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds the js needed to draw a red dot on the screen with the specified options.
|
||||
/// </summary>
|
||||
/// <param name="x">The x coordinate of the dot</param>
|
||||
/// <param name="y">The y coordinate of the dot</param>
|
||||
/// <param name="thickness">The thickness of the dot</param>
|
||||
/// <param name="id">The id of the created div element</param>
|
||||
/// <returns>The script to execute inside the the browser</returns>
|
||||
private static string RedDotScript(int x, int y, int thickness, string id = "reddot")
|
||||
{
|
||||
return " var div = document.createElement('div');" +
|
||||
" div.style.backgroundColor = 'red';" +
|
||||
" div.id = '" + id + "';" +
|
||||
" div.style.position = 'absolute';" +
|
||||
" div.style.left = '" + x + "px';" +
|
||||
" div.style.top = '" + y + "px';" +
|
||||
" div.style.height = '" + thickness + "px';" +
|
||||
" div.style.width = '" + thickness + "px';" +
|
||||
" document.getElementsByTagName('body')[0].appendChild(div);";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Spawns a 1x1 div element at the specified coordinates.
|
||||
/// </summary>
|
||||
/// /// <param name="driver">The selenium driver</param>
|
||||
/// <param name="x">The x coordinate of the div</param>
|
||||
/// <param name="y">The y coordinate of the div</param>
|
||||
/// /// <param name="id">The id of the div element</param>
|
||||
private static void SpawnDiv(RemoteWebDriver driver, int x, int y, string id)
|
||||
{
|
||||
try
|
||||
{
|
||||
var executor = driver as IJavaScriptExecutor;
|
||||
executor.ExecuteScript(RedDotScript(x, y, 1, id));
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draws an array of red dots on the screen.
|
||||
/// </summary>
|
||||
/// <param name="driver">The selenium driver</param>
|
||||
/// <param name="points">The array of points to draw</param>
|
||||
/// <param name="thickness">The thickness of the points</param>
|
||||
public static void DrawRedDots(RemoteWebDriver driver, Point[] points, int thickness)
|
||||
{
|
||||
var script = "";
|
||||
for (int i = 0; i < points.Count(); i++)
|
||||
script += RedDotScript(points[i].X, points[i].Y, thickness);
|
||||
|
||||
try
|
||||
{
|
||||
var executor = driver as IJavaScriptExecutor;
|
||||
executor.ExecuteScript(script);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses an html element from LoliScript code.
|
||||
/// </summary>
|
||||
/// <param name="input">The reference to the line of code</param>
|
||||
/// <param name="data">The BotData needed for variable replacement</param>
|
||||
/// <returns>The parsed IWebElement</returns>
|
||||
public static IWebElement ParseElement(ref string input, BotData data)
|
||||
{
|
||||
LineParser.EnsureIdentifier(ref input, "ELEMENT");
|
||||
var locator = (ElementLocator)LineParser.ParseEnum(ref input, "Element Locator", typeof(ElementLocator));
|
||||
var elemstring = LineParser.ParseLiteral(ref input, "Element Identifier");
|
||||
var index = 0;
|
||||
if (LineParser.Lookahead(ref input) == TokenType.Integer)
|
||||
index = LineParser.ParseInt(ref input, "Element Index");
|
||||
switch (locator)
|
||||
{
|
||||
case ElementLocator.Id:
|
||||
return data.Driver.FindElementsById(elemstring)[index];
|
||||
|
||||
case ElementLocator.Class:
|
||||
return data.Driver.FindElementsByClassName(elemstring)[index];
|
||||
|
||||
case ElementLocator.Name:
|
||||
return data.Driver.FindElementsByName(elemstring)[index];
|
||||
|
||||
case ElementLocator.Selector:
|
||||
return data.Driver.FindElementsByCssSelector(elemstring)[index];
|
||||
|
||||
case ElementLocator.Tag:
|
||||
return data.Driver.FindElementsByTagName(elemstring)[index];
|
||||
|
||||
case ElementLocator.XPath:
|
||||
return data.Driver.FindElementsByXPath(elemstring)[index];
|
||||
|
||||
default:
|
||||
throw new Exception("Element not found on the page");
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
#region Line Class
|
||||
/// <summary>
|
||||
/// <para>Represents a line drawn between two points.</para>
|
||||
/// <para>This class provides useful methods to simulate mouse movement across the screen.</para>
|
||||
/// </summary>
|
||||
public class Line
|
||||
{
|
||||
private Point p1, p2;
|
||||
private Random rand;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a line between two points.
|
||||
/// </summary>
|
||||
/// <param name="p1"></param>
|
||||
/// <param name="p2"></param>
|
||||
public Line(Point p1, Point p2)
|
||||
{
|
||||
this.p1 = p1;
|
||||
this.p2 = p2;
|
||||
rand = new Random();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a given number of equally distant points in the line.
|
||||
/// </summary>
|
||||
/// <param name="quantity">The amount of points to generate</param>
|
||||
/// <returns>The array of the generated points</returns>
|
||||
public Point[] getPoints(int quantity)
|
||||
{
|
||||
var points = new Point[quantity];
|
||||
int ydiff = p2.Y - p1.Y, xdiff = p2.X - p1.X;
|
||||
double slope = (double)(p2.Y - p1.Y) / (p2.X - p1.X);
|
||||
double x, y;
|
||||
|
||||
--quantity;
|
||||
|
||||
for (double i = 0; i < quantity; i++)
|
||||
{
|
||||
y = slope == 0 ? 0 : ydiff * (i / quantity);
|
||||
x = slope == 0 ? xdiff * (i / quantity) : y / slope;
|
||||
points[(int)i] = new Point((int)Math.Round(x) + p1.X, (int)Math.Round(y) + p1.Y);
|
||||
}
|
||||
|
||||
points[quantity] = p2;
|
||||
return points;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a given number of offsets from equally distant points in the line.
|
||||
/// </summary>
|
||||
/// <param name="quantity">The amount of offsets to generate</param>
|
||||
/// <returns>The array of the generated offsets</returns>
|
||||
public Point[] getOffsets(int quantity)
|
||||
{
|
||||
var points = new Point[quantity];
|
||||
int ydiff = p2.Y - p1.Y, xdiff = p2.X - p1.X;
|
||||
double slope = (double)(p2.Y - p1.Y) / (p2.X - p1.X);
|
||||
double x, y;
|
||||
|
||||
--quantity;
|
||||
|
||||
for (double i = 0; i < quantity; i++)
|
||||
{
|
||||
y = slope == 0 ? 0 : ydiff * (i / quantity);
|
||||
x = slope == 0 ? xdiff * (i / quantity) : y / slope;
|
||||
points[(int)i] = new Point((int)Math.Round(x), (int)Math.Round(y));
|
||||
}
|
||||
|
||||
points[quantity] = p2;
|
||||
return points;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The carthesian distance between two points.
|
||||
/// </summary>
|
||||
/// <param name="x1">The x coordinate of the first point</param>
|
||||
/// <param name="y1">The y coordinate of the first point</param>
|
||||
/// <param name="x2">The x coordinate of the second point</param>
|
||||
/// <param name="y2">The y coordinate of the second point</param>
|
||||
/// <returns>The distance between the points</returns>
|
||||
private static double Distance(double x1, double y1, double x2, double y2)
|
||||
{
|
||||
return Math.Sqrt(Math.Pow(x2 - x1, 2) + Math.Pow(y2 - y1, 2));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the hypotenuse of a triangle given its legs.
|
||||
/// </summary>
|
||||
/// <param name="x">The first leg of the triangle</param>
|
||||
/// <param name="y">The second leg of the triangle</param>
|
||||
/// <returns>The value of the hypotenuse</returns>
|
||||
private static double Hypot(double x, double y)
|
||||
{
|
||||
return Math.Sqrt(Math.Pow(x, 2) + Math.Pow(y, 2));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the points drawn by a mouse when a human moves it across the screen.
|
||||
/// </summary>
|
||||
/// <param name="xs">The x coordinate of the starting point</param>
|
||||
/// <param name="ys">The y coordinate of the starting point</param>
|
||||
/// <param name="xe">The x coordinate of the ending point</param>
|
||||
/// <param name="ye">The y coordinate of the ending point</param>
|
||||
/// <param name="gravity">The gravity the movement is subject to</param>
|
||||
/// <param name="wind">The non linearity of the movement</param>
|
||||
/// <param name="targetArea">The target area to reach</param>
|
||||
/// <returns>The set of points to draw in order to emulate a humanlike movement of the mouse</returns>
|
||||
public Point[] HumanWindMouse(double xs, double ys, double xe, double ye, double gravity, double wind, double targetArea)
|
||||
{
|
||||
double veloX = 0,
|
||||
veloY = 0,
|
||||
windX = 0,
|
||||
windY = 0;
|
||||
|
||||
var sqrt2 = Math.Sqrt(2);
|
||||
var sqrt3 = Math.Sqrt(3);
|
||||
var sqrt5 = Math.Sqrt(5);
|
||||
|
||||
var tDist = (int)Distance(Math.Round(xs), Math.Round(ys), Math.Round(xe), Math.Round(ye));
|
||||
var t = (uint)(Environment.TickCount + 10000);
|
||||
|
||||
List<Point> points = new List<Point>();
|
||||
do
|
||||
{
|
||||
if (Environment.TickCount > t)
|
||||
break;
|
||||
|
||||
var dist = Hypot(xs - xe, ys - ye);
|
||||
wind = Math.Min(wind, dist);
|
||||
|
||||
if (dist < 1)
|
||||
dist = 1;
|
||||
|
||||
var d = (Math.Round(Math.Round((double)tDist) * 0.3) / 7);
|
||||
|
||||
if (d > 25)
|
||||
d = 25;
|
||||
|
||||
if (d < 5)
|
||||
d = 5;
|
||||
|
||||
double rCnc = rand.Next(6);
|
||||
|
||||
if (rCnc == 1)
|
||||
d = 2;
|
||||
|
||||
double maxStep;
|
||||
|
||||
if (d <= Math.Round(dist))
|
||||
maxStep = d;
|
||||
else
|
||||
maxStep = Math.Round(dist);
|
||||
|
||||
if (dist >= targetArea)
|
||||
{
|
||||
windX = windX / sqrt3 + (rand.Next((int)(Math.Round(wind) * 2 + 1)) - wind) / sqrt5;
|
||||
windY = windY / sqrt3 + (rand.Next((int)(Math.Round(wind) * 2 + 1)) - wind) / sqrt5;
|
||||
}
|
||||
else
|
||||
{
|
||||
windX = windX / sqrt2;
|
||||
windY = windY / sqrt2;
|
||||
}
|
||||
|
||||
veloX = veloX + windX;
|
||||
veloY = veloY + windY;
|
||||
veloX = veloX + gravity * (xe - xs) / dist;
|
||||
veloY = veloY + gravity * (ye - ys) / dist;
|
||||
|
||||
if (Hypot(veloX, veloY) > maxStep)
|
||||
{
|
||||
var randomDist = maxStep / 2.0 + rand.Next((int)(Math.Round(maxStep) / 2));
|
||||
var veloMag = Math.Sqrt(veloX * veloX + veloY * veloY);
|
||||
veloX = (veloX / veloMag) * randomDist;
|
||||
veloY = (veloY / veloMag) * randomDist;
|
||||
}
|
||||
|
||||
var lastX = (int)Math.Round(xs);
|
||||
var lastY = (int)Math.Round(ys);
|
||||
xs = xs + veloX;
|
||||
ys = ys + veloY;
|
||||
|
||||
// Set cursor on next step
|
||||
if (lastX != Math.Round(xs) || (lastY != Math.Round(ys)))
|
||||
points.Add(new Point((int)Math.Round(xs), (int)Math.Round(ys)));
|
||||
|
||||
} while (!(Hypot(xs - xe, ys - ye) < 1));
|
||||
|
||||
// Set cursor on end
|
||||
if (Math.Round(xe) != Math.Round(xs) || (Math.Round(ye) != Math.Round(ys)))
|
||||
points.Add(new Point((int)Math.Round(xe), (int)Math.Round(ye)));
|
||||
|
||||
return points.ToArray();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
using Extreme.Net;
|
||||
using RuriLib.Models;
|
||||
using System;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace RuriLib.LS
|
||||
{
|
||||
/// <summary>
|
||||
/// Parses a SET command.
|
||||
/// </summary>
|
||||
class SetParser
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the Action that needs to be executed.
|
||||
/// </summary>
|
||||
/// <param name="line">The data line to parse</param>
|
||||
/// <param name="data">The BotData needed for variable replacement</param>
|
||||
/// <returns>The Action to execute</returns>
|
||||
public static Action Parse(string line, BotData data)
|
||||
{
|
||||
var input = line.Trim();
|
||||
var field = LineParser.ParseToken(ref input, TokenType.Parameter, true).ToUpper();
|
||||
|
||||
return new Action(() =>
|
||||
{
|
||||
switch (field)
|
||||
{
|
||||
case "SOURCE":
|
||||
data.ResponseSource = LineParser.ParseLiteral(ref input, "SOURCE", true, data);
|
||||
break;
|
||||
|
||||
case "STATUS":
|
||||
data.Status = (BotStatus)LineParser.ParseEnum(ref input, "STATUS", typeof(BotStatus));
|
||||
if (data.Status == BotStatus.CUSTOM) data.CustomStatus = LineParser.ParseLiteral(ref input, "CUSTOM STATUS");
|
||||
break;
|
||||
|
||||
case "RESPONSECODE":
|
||||
data.ResponseCode = LineParser.ParseInt(ref input, "RESPONSECODE").ToString();
|
||||
break;
|
||||
|
||||
case "COOKIE":
|
||||
var name = LineParser.ParseLiteral(ref input, "NAME", true, data);
|
||||
data.Cookies.Add(name, LineParser.ParseLiteral(ref input, "VALUE", true, data));
|
||||
break;
|
||||
|
||||
case "ADDRESS":
|
||||
data.Address = LineParser.ParseLiteral(ref input, "ADDRESS", true, data);
|
||||
break;
|
||||
|
||||
case "PROXY":
|
||||
data.Proxy = new CProxy(LineParser.ParseLiteral(ref input, "PROXY", true, data), data.Proxy.Type);
|
||||
break;
|
||||
|
||||
case "PROXYTYPE":
|
||||
data.Proxy.Type = (ProxyType)LineParser.ParseEnum(ref input, "PROXYTYPE", typeof(ProxyType));
|
||||
break;
|
||||
|
||||
case "DATA":
|
||||
data.Data = new CData(LineParser.ParseLiteral(ref input, "DATA", true, data), new WordlistType());
|
||||
break;
|
||||
|
||||
case "VAR":
|
||||
data.Variables.Set(new CVar(
|
||||
LineParser.ParseLiteral(ref input, "NAME", true, data),
|
||||
LineParser.ParseLiteral(ref input, "VALUE", true, data),
|
||||
false
|
||||
));
|
||||
break;
|
||||
|
||||
case "CAP":
|
||||
data.Variables.Set(new CVar(
|
||||
LineParser.ParseLiteral(ref input, "NAME", true, data),
|
||||
LineParser.ParseLiteral(ref input, "VALUE", true, data),
|
||||
true
|
||||
));
|
||||
break;
|
||||
|
||||
case "GVAR":
|
||||
try
|
||||
{
|
||||
data.GlobalVariables.Set(new CVar(
|
||||
LineParser.ParseLiteral(ref input, "NAME", true, data),
|
||||
LineParser.ParseLiteral(ref input, "VALUE", true, data),
|
||||
false
|
||||
));
|
||||
}
|
||||
catch { }
|
||||
break;
|
||||
|
||||
case "NEWGVAR":
|
||||
try
|
||||
{
|
||||
data.GlobalVariables.SetNew(new CVar(
|
||||
LineParser.ParseLiteral(ref input, "NAME", true, data),
|
||||
LineParser.ParseLiteral(ref input, "VALUE", true, data),
|
||||
false
|
||||
));
|
||||
}
|
||||
catch { }
|
||||
break;
|
||||
|
||||
case "GCOOKIES":
|
||||
data.GlobalCookies.Clear();
|
||||
foreach (var cookie in data.Cookies)
|
||||
data.GlobalCookies.Add(cookie.Key, cookie.Value);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new ArgumentException($"Invalid identifier {field}");
|
||||
}
|
||||
|
||||
data.Log(new LogEntry($"SET command executed on field {field}", Colors.White));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace RuriLib.LS
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides useful functions for writing a block as a piece of LoliScript code.
|
||||
/// </summary>
|
||||
public class BlockWriter : StringWriter
|
||||
{
|
||||
private bool Indented { get; set; }
|
||||
private Type Type { get; set; }
|
||||
private object Block { get; set; }
|
||||
private bool Disabled { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new BlockWriter for the given block type and with the given settings.
|
||||
/// </summary>
|
||||
/// <param name="blockType">The Type of the block to write</param>
|
||||
/// <param name="indented">Whether the code can be indented on multiple lines for readability</param>
|
||||
/// <param name="disabled">Whether the block to write is disabled</param>
|
||||
public BlockWriter(Type blockType, bool indented = true, bool disabled = false)
|
||||
{
|
||||
Type = blockType;
|
||||
Block = Activator.CreateInstance(blockType);
|
||||
Indented = indented;
|
||||
Disabled = disabled;
|
||||
if (Disabled) Write("!");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes any type of variable as a token (by calling its default ToString method) and a space.
|
||||
/// </summary>
|
||||
/// <param name="token">The variable to write</param>
|
||||
/// <param name="property">The name of the property of the block. If the value is the default one, it will not be written. Do not set this parameter to always write the variable.</param>
|
||||
/// <returns>The BlockWriter itself</returns>
|
||||
public BlockWriter Token(dynamic token, string property = "")
|
||||
{
|
||||
if (property != "" && CheckDefault(token, property)) return this;
|
||||
Write($"{token.ToString()} ");
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes an integer value and a space.
|
||||
/// </summary>
|
||||
/// <param name="integer">The integer value to write</param>
|
||||
/// <param name="property">The name of the property of the block. If the value is the default one, it will not be written. Do not set this parameter to always write the variable.</param>
|
||||
/// <returns>The BlockWriter itself</returns>
|
||||
public BlockWriter Integer(int integer, string property = "")
|
||||
{
|
||||
if (property != "" && CheckDefault(integer, property)) return this;
|
||||
Write($"{integer} ");
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a literal value (with escaped double-quotes) wrapped by double-quotes and a space.
|
||||
/// </summary>
|
||||
/// <param name="literal">The literal value to write</param>
|
||||
/// <param name="property">The name of the property of the block. If the value is the default one, it will not be written. Do not set this parameter to always write the variable.</param>
|
||||
/// <returns>The BlockWriter itself</returns>
|
||||
public BlockWriter Literal(string literal, string property = "")
|
||||
{
|
||||
if (property != "" && CheckDefault(literal, property)) return this;
|
||||
Write($"\"{literal.Replace("\"", "\\\"")}\" ");
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes an Arrow (->) and a space.
|
||||
/// </summary>
|
||||
/// <returns>The BlockWriter itself</returns>
|
||||
public BlockWriter Arrow()
|
||||
{
|
||||
Write("-> ");
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a block label as a # sign, the label name and a space.
|
||||
/// </summary>
|
||||
/// <param name="label">The label of the block</param>
|
||||
/// <returns>The BlockWriter itself</returns>
|
||||
public BlockWriter Label(string label)
|
||||
{
|
||||
if (CheckDefault(label, "Label")) return this;
|
||||
Write($"#{label.Replace(" ", "_")} ");
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a boolean with the syntax Name=Value and a space.
|
||||
/// </summary>
|
||||
/// <param name="boolean">The boolean value to write</param>
|
||||
/// <param name="property">The name of the property</param>
|
||||
/// <returns></returns>
|
||||
public BlockWriter Boolean(bool boolean, string property)
|
||||
{
|
||||
if (property != "" && CheckDefault(boolean, property)) return this;
|
||||
Write($"{property}={boolean.ToString().ToUpper()} ");
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a linebreak and a given number of spaces on the next line.
|
||||
/// </summary>
|
||||
/// <param name="spacing">The amount of spacing to perform. A spacing of value 1 means two blank space characters, 2 means 4 etc.</param>
|
||||
/// <returns></returns>
|
||||
public BlockWriter Indent(int spacing = 1)
|
||||
{
|
||||
if (Indented)
|
||||
{
|
||||
WriteLine();
|
||||
if (Disabled) Write("!");
|
||||
for (var i = 0; i < spacing; i++)
|
||||
Write(" ");
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a linebreak.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public BlockWriter Return()
|
||||
{
|
||||
WriteLine();
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a property has the default value.
|
||||
/// </summary>
|
||||
/// <param name="value">The value that needs to be checked</param>
|
||||
/// <param name="property">The name of the property that contains that value</param>
|
||||
/// <returns>Whether the property has the default value or not</returns>
|
||||
public bool CheckDefault(object value, string property)
|
||||
{
|
||||
var prop = Type.GetProperty(property);
|
||||
var val = prop.GetValue(Block);
|
||||
return value.Equals(val);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,223 @@
|
||||
using Extreme.Net;
|
||||
using OpenQA.Selenium.Remote;
|
||||
using RuriLib.Models;
|
||||
using RuriLib.ViewModels;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Security;
|
||||
using System.Net.Sockets;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace RuriLib
|
||||
{
|
||||
/// <summary>
|
||||
/// The status of the Bot. While the status is NONE or SUCCESS the Bot keeps checking the script until the end, otherwise it stops.
|
||||
/// </summary>
|
||||
public enum BotStatus
|
||||
{
|
||||
/// <summary>The initial status of the bot. If a bot ends its execution with this stauts, the data line will be registered as ToCheck.</summary>
|
||||
NONE,
|
||||
|
||||
/// <summary>Something wrong happened while processing a block.</summary>
|
||||
ERROR,
|
||||
|
||||
/// <summary>The data line will be registered as a Hit.</summary>
|
||||
SUCCESS,
|
||||
|
||||
/// <summary>The data line will be registered as a fail.</summary>
|
||||
FAIL,
|
||||
|
||||
/// <summary>The data line will be retried and the proxy will be banned.</summary>
|
||||
BAN,
|
||||
|
||||
/// <summary>The data line will be retried.</summary>
|
||||
RETRY,
|
||||
|
||||
/// <summary>The data line will be registered as a Custom.</summary>
|
||||
CUSTOM
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class that holds all the variables needed for checking a single data line.
|
||||
/// </summary>
|
||||
public class BotData
|
||||
{
|
||||
/// <summary>A random number generator that methods can refer to, in order to avoid the generation of the same random number if executed in rapid succession.</summary>
|
||||
public Random rand = new Random();
|
||||
|
||||
/// <summary>The Status of the Bot.</summary>
|
||||
public BotStatus Status { get; set; }
|
||||
|
||||
/// <summary>The Custom Status name in case the Status was set to Custom.</summary>
|
||||
public string CustomStatus = "CUSTOM";
|
||||
|
||||
/// <summary>The Status of the Bot as a string.</summary>
|
||||
public string StatusString { get { return Status == BotStatus.CUSTOM ? CustomStatus : Status.ToString(); } }
|
||||
|
||||
/// <summary>The id of the current Bot.</summary>
|
||||
public int BotNumber { get; set; }
|
||||
|
||||
/// <summary>The captcha service currently being used.</summary>
|
||||
public CaptchaServices.CaptchaService CaptchaService { get; set; }
|
||||
|
||||
/// <summary>The selenium webdriver currently being used.</summary>
|
||||
public RemoteWebDriver Driver { get; set; }
|
||||
|
||||
/// <summary>Whether the browser is open or not.</summary>
|
||||
public bool BrowserOpen { get; set; }
|
||||
|
||||
/// <summary>The TCP Client used for TCP blocks.</summary>
|
||||
public TcpClient TCPClient { get; set; }
|
||||
|
||||
/// <summary>The NET Stream used for TCP blocks with unencrypted connection.</summary>
|
||||
public NetworkStream NETStream { get; set; }
|
||||
|
||||
/// <summary>The SSL Stream used for TCP blocks with encrypted connection.</summary>
|
||||
public SslStream SSLStream { get; set; }
|
||||
|
||||
/// <summary>Whether to use SSL for the current TCPClient.</summary>
|
||||
public bool TCPSSL { get; set; } = true;
|
||||
|
||||
/// <summary>The wrapped data line that needs to be checked.</summary>
|
||||
public CData Data { get; set; }
|
||||
|
||||
/// <summary>The proxy currently being used (null if none).</summary>
|
||||
public CProxy Proxy { get; set; }
|
||||
|
||||
/// <summary>Whether we should use proxies or not.</summary>
|
||||
public bool UseProxies { get; set; }
|
||||
|
||||
/// <summary>The Global RuriLib settings valid for any Config.</summary>
|
||||
public RLSettingsViewModel GlobalSettings { get; set; }
|
||||
|
||||
/// <summary>The settings for the current Config.</summary>
|
||||
public ConfigSettings ConfigSettings { get; set; }
|
||||
|
||||
/// <summary>The remaining balance on the captcha service account.</summary>
|
||||
public double Balance { get; set; }
|
||||
|
||||
/// <summary>The paths of the Screenshots saved by selenium.</summary>
|
||||
public List<string> Screenshots { get; set; }
|
||||
|
||||
#region Response
|
||||
/// <summary>The return Address of the HttpResponse.</summary>
|
||||
public string Address {
|
||||
get { return Variables.Get("ADDRESS").Value; }
|
||||
set { Variables.SetHidden("ADDRESS", value); }
|
||||
}
|
||||
|
||||
/// <summary>The HTTP code of the HttpResponse.</summary>
|
||||
public string ResponseCode
|
||||
{
|
||||
get { return Variables.Get("RESPONSECODE").Value; }
|
||||
set { Variables.SetHidden("RESPONSECODE", value); }
|
||||
}
|
||||
|
||||
/// <summary>The dictionary of headers of the HttpResponse.</summary>
|
||||
public Dictionary<string, string> ResponseHeaders
|
||||
{
|
||||
get { return Variables.Get("HEADERS").Value; }
|
||||
set { Variables.SetHidden("HEADERS", value); }
|
||||
}
|
||||
|
||||
/// <summary>The dictionary of cookies of the HttpResponse.</summary>
|
||||
public Dictionary<string, string> Cookies
|
||||
{
|
||||
get { return Variables.Get("COOKIES").Value; }
|
||||
set { Variables.SetHidden("COOKIES", value); }
|
||||
}
|
||||
|
||||
/// <summary>The source of the HttpResponse.</summary>
|
||||
public string ResponseSource
|
||||
{
|
||||
get { return Variables.Get("SOURCE").Value; }
|
||||
set { Variables.SetHidden("SOURCE", value); }
|
||||
}
|
||||
#endregion
|
||||
|
||||
// Variables and capture
|
||||
/// <summary>The local variables of the Bot.</summary>
|
||||
public VariableList Variables { get; set; }
|
||||
|
||||
/// <summary>The global variables shared between all the Bots. They can be accessed or set by any bot.</summary>
|
||||
public VariableList GlobalVariables { get; set; }
|
||||
|
||||
/// <summary>The global cookies shared between all the Bots. They are set in the local cookie jar at the start of the check.</summary>
|
||||
public Dictionary<string, string> GlobalCookies { get; set; }
|
||||
|
||||
// Log
|
||||
/// <summary>The detailed log of the last block that was run.</summary>
|
||||
public List<LogEntry> LogBuffer { get; set; }
|
||||
|
||||
/// <summary>Whether this object is being used for a Debugger or for the Runner.</summary>
|
||||
public bool IsDebug { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a BotData object given some parameters.
|
||||
/// </summary>
|
||||
/// <param name="globalSettings">The global RuriLib settings</param>
|
||||
/// <param name="configSettings">The settings for the current Config</param>
|
||||
/// <param name="data">The wrapped data line to check</param>
|
||||
/// <param name="proxy">The proxy to use (set to null if none)</param>
|
||||
/// <param name="useProxies">Whether to use the proxy for requests</param>
|
||||
/// <param name="botNumber">The number of the bot that is creating this object</param>
|
||||
/// <param name="isDebug">Whether this object is created from a Debugger or from a Runner</param>
|
||||
public BotData(RLSettingsViewModel globalSettings, ConfigSettings configSettings, CData data, CProxy proxy, bool useProxies, int botNumber = 0, bool isDebug = true)
|
||||
{
|
||||
Data = data;
|
||||
Proxy = proxy;
|
||||
UseProxies = useProxies;
|
||||
Status = BotStatus.NONE;
|
||||
BotNumber = BotNumber;
|
||||
GlobalSettings = globalSettings;
|
||||
ConfigSettings = configSettings;
|
||||
Balance = 0;
|
||||
Screenshots = new List<string>();
|
||||
Variables = new VariableList();
|
||||
|
||||
// Populate the Variables list with hidden variables
|
||||
Address = "";
|
||||
ResponseCode = "0";
|
||||
ResponseSource = "";
|
||||
Cookies = new Dictionary<string, string>();
|
||||
ResponseHeaders = new Dictionary<string, string>();
|
||||
try { foreach (var v in Data.GetVariables()) Variables.Set(v); } catch { }
|
||||
|
||||
GlobalVariables = new VariableList();
|
||||
GlobalCookies = new CookieDictionary();
|
||||
LogBuffer = new List<LogEntry>();
|
||||
Driver = null;
|
||||
BrowserOpen = false;
|
||||
IsDebug = isDebug;
|
||||
BotNumber = botNumber;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new LogEntry to the LogBuffer.
|
||||
/// </summary>
|
||||
/// <param name="entry">The LogEntry to add</param>
|
||||
public void Log(LogEntry entry)
|
||||
{
|
||||
if (GlobalSettings.General.EnableBotLog || IsDebug)
|
||||
LogBuffer.Add(entry);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds multiple new LogEntry objects to the LogBuffer.
|
||||
/// </summary>
|
||||
/// <param name="list">The list of log entries to add</param>
|
||||
public void LogRange(List<LogEntry> list)
|
||||
{
|
||||
if (GlobalSettings.General.EnableBotLog || IsDebug)
|
||||
LogBuffer.AddRange(list);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logs an empty line to the LogBuffer.
|
||||
/// </summary>
|
||||
public void LogNewLine()
|
||||
{
|
||||
LogBuffer.Add(new LogEntry("", Colors.White));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace RuriLib.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a data line that needs to be checked.
|
||||
/// </summary>
|
||||
public class CData
|
||||
{
|
||||
/// <summary>The actual content of the line.</summary>
|
||||
public string Data { get; set; }
|
||||
|
||||
/// <summary>The WordlistType of the Wordlist the line belongs to.</summary>
|
||||
public WordlistType Type { get; set; }
|
||||
|
||||
/// <summary>Whether the data line respects the regex verification.</summary>
|
||||
public bool IsValid
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Type.Verify) return Regex.Match(Data, Type.Regex).Success;
|
||||
else return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a CData object given a data line and the WordlistType.
|
||||
/// </summary>
|
||||
/// <param name="data">The data line</param>
|
||||
/// <param name="type">The WordlistType of the Wordlist the data line belongs to</param>
|
||||
public CData(string data, WordlistType type)
|
||||
{
|
||||
Data = data;
|
||||
Type = type;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all the variables that need to be set after slicing the data line.
|
||||
/// </summary>
|
||||
/// <returns>The variables that need to be set inside the Bot's VariableList</returns>
|
||||
public List<CVar> GetVariables()
|
||||
{
|
||||
return Data
|
||||
.Split(new string[] { Type.Separator }, StringSplitOptions.None)
|
||||
.Zip(Type.Slices, (k, v) => new { k, v })
|
||||
.Select(x => new CVar(x.v, x.k, false, true))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the data line respects all the data rules present in a Config.
|
||||
/// </summary>
|
||||
/// <param name="rules">The list of rules of the Config</param>
|
||||
/// <returns>Whether the data line respects the rules</returns>
|
||||
public bool RespectsRules(List<DataRule> rules)
|
||||
{
|
||||
var valid = true;
|
||||
var variables = GetVariables();
|
||||
foreach(var rule in rules)
|
||||
{
|
||||
try
|
||||
{
|
||||
var slice = (string)variables.FirstOrDefault(v => v.Name == rule.SliceName).Value;
|
||||
|
||||
switch (rule.RuleType)
|
||||
{
|
||||
case RuleType.MinLength:
|
||||
valid = slice.Length >= int.Parse(rule.RuleString);
|
||||
break;
|
||||
|
||||
case RuleType.MaxLength:
|
||||
valid = slice.Length <= int.Parse(rule.RuleString);
|
||||
break;
|
||||
|
||||
case RuleType.MustContain:
|
||||
valid = CheckContains(slice, rule.RuleString);
|
||||
break;
|
||||
|
||||
case RuleType.MustNotContain:
|
||||
valid = !CheckContains(slice, rule.RuleString);
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
return valid;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a data line contains certain characters.
|
||||
/// </summary>
|
||||
/// <param name="input">The data line</param>
|
||||
/// <param name="what">The characters to search. Defaults are Lowercase, Uppercase, Digit, Symbol. Custom character sets can be created by concatenating the characters in a single string (e.g. ABCDEF)</param>
|
||||
/// <returns></returns>
|
||||
private static bool CheckContains(string input, string what)
|
||||
{
|
||||
switch (what)
|
||||
{
|
||||
case "Lowercase":
|
||||
return input.Any(c => char.IsLower(c));
|
||||
|
||||
case "Uppercase":
|
||||
return input.Any(c => char.IsUpper(c));
|
||||
|
||||
case "Digit":
|
||||
return input.Any(c => char.IsDigit(c));
|
||||
|
||||
case "Symbol":
|
||||
return input.Any(c => char.IsSymbol(c));
|
||||
|
||||
default:
|
||||
foreach(var c in what.ToCharArray())
|
||||
{
|
||||
if (input.Contains(c)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,259 @@
|
||||
using Extreme.Net;
|
||||
using LiteDB;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace RuriLib.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// The Status of a CProxy.
|
||||
/// </summary>
|
||||
public enum Status
|
||||
{
|
||||
/// <summary>The proxy is not assigned to any bot.</summary>
|
||||
AVAILABLE,
|
||||
|
||||
/// <summary>The proxy is assigned to at least 1 bot.</summary>
|
||||
BUSY,
|
||||
|
||||
/// <summary>The proxy didn't accept the connection.</summary>
|
||||
BAD,
|
||||
|
||||
/// <summary>The proxy was banned.</summary>
|
||||
BANNED
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The Working Status of a CProxy.
|
||||
/// </summary>
|
||||
public enum ProxyWorking
|
||||
{
|
||||
/// <summary>The proxy is working fine.</summary>
|
||||
YES,
|
||||
|
||||
/// <summary>The proxy is down or not accepting connections.</summary>
|
||||
NO,
|
||||
|
||||
/// <summary>The proxy was not tested.</summary>
|
||||
UNTESTED
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A proxy that supports http(s) and socks4/4a/5 protocols, authorization and chaining.
|
||||
/// </summary>
|
||||
public class CProxy
|
||||
{
|
||||
/// <summary>Needed for NoSQL storage.</summary>
|
||||
public Guid Id { get; set; }
|
||||
|
||||
/// <summary>The unparsed proxy string.</summary>
|
||||
public string Proxy { get; set; } = "";
|
||||
|
||||
/// <summary>The username used for authentication (empty if none).</summary>
|
||||
public string Username { get; set; } = "";
|
||||
|
||||
/// <summary>The password used for authentication (empty if none).</summary>
|
||||
public string Password { get; set; } = "";
|
||||
|
||||
/// <summary>The type of proxy.</summary>
|
||||
public ProxyType Type { get; set; } = ProxyType.Http;
|
||||
|
||||
/// <summary>The country of the proxy's ip.</summary>
|
||||
public string Country { get; set; } = "";
|
||||
|
||||
/// <summary>The response delay of the proxy.</summary>
|
||||
public int Ping { get; set; } = 0;
|
||||
|
||||
/// <summary>The next proxy object in a Proxy Chain.</summary>
|
||||
public CProxy Next { get; set; } = null;
|
||||
|
||||
/// <summary>When the proxy was last used.</summary>
|
||||
public DateTime LastUsed { get; set; } = new DateTime(1970, 1, 1);
|
||||
|
||||
/// <summary>When the proxy was last checked.</summary>
|
||||
public DateTime LastChecked { get; set; } = new DateTime(1970, 1, 1);
|
||||
|
||||
/// <summary>The Working Status of the proxy.</summary>
|
||||
public ProxyWorking Working { get; set; } = ProxyWorking.UNTESTED;
|
||||
|
||||
/// <summary>Whether the proxy has a successor in the Proxy Chain.</summary>
|
||||
[BsonIgnore]
|
||||
public bool HasNext { get { return Next != null; } }
|
||||
|
||||
/// <summary>The number of times the proxy was used for a check.</summary>
|
||||
[BsonIgnore]
|
||||
public int Uses { get; set; } = 0;
|
||||
|
||||
/// <summary>The number of bots the proxy is hooked to.</summary>
|
||||
[BsonIgnore]
|
||||
public int Hooked { get; set; } = 0;
|
||||
|
||||
/// <summary>The clearance cookie from Cloudflare.</summary>
|
||||
[BsonIgnore]
|
||||
public string Clearance { get; set; } = "";
|
||||
|
||||
/// <summary>The cfduid cookie from Cloudflare.</summary>
|
||||
[BsonIgnore]
|
||||
public string Cfduid { get; set; } = "";
|
||||
|
||||
/// <summary>The status of the proxy.</summary>
|
||||
[BsonIgnore]
|
||||
public Status Status { get; set; } = Status.AVAILABLE;
|
||||
|
||||
/// <summary>Needed for NoSQL deserialization.</summary>
|
||||
public CProxy() { }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a proxy given a string and a proxy type.
|
||||
/// </summary>
|
||||
/// <param name="proxy">The unparsed proxy string</param>
|
||||
/// <param name="type">The proxy type</param>
|
||||
public CProxy(string proxy, ProxyType type)
|
||||
{
|
||||
Proxy = proxy;
|
||||
Type = type;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses a CProxy object from an advanced string. Supports Proxy Chains.
|
||||
/// </summary>
|
||||
/// <param name="proxy">The string to parse the proxy from</param>
|
||||
/// <returns>The parsed CProxy object</returns>
|
||||
public CProxy Parse(string proxy)
|
||||
{
|
||||
// Take the first proxy of the chain
|
||||
var chain = proxy.Split(new string[] { "->" }, 2, StringSplitOptions.None);
|
||||
var current = chain[0];
|
||||
|
||||
// If the type was specified, parse it and remove it from the string
|
||||
if (current.StartsWith("("))
|
||||
{
|
||||
var groups = Regex.Match(current, @"^\((.*)\)(.*)").Groups;
|
||||
Enum.TryParse<ProxyType>(groups[1].Value, true, out var type);
|
||||
Type = type;
|
||||
current = current.Replace($"({groups[1].Value})", "");
|
||||
}
|
||||
|
||||
// Split the host:port:user:pass portion and set the first two
|
||||
var fields = current.Split(':');
|
||||
Proxy = $"{fields[0]}:{fields[1]}";
|
||||
|
||||
// Set the other two if they are present
|
||||
if (fields.Count() > 2)
|
||||
{
|
||||
Username = fields[2];
|
||||
Password = fields[3];
|
||||
}
|
||||
|
||||
// If the chain is not finished, set the next proxy by parsing recursively
|
||||
if (chain.Count() > 1)
|
||||
{
|
||||
Next = (new CProxy()).Parse(chain[1]);
|
||||
}
|
||||
|
||||
// Finally return this proxy
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ProxyClient related to the specific proxy type.
|
||||
/// </summary>
|
||||
/// <returns>The ProxyClient to be used in a HttpRequest</returns>
|
||||
public ProxyClient GetClient()
|
||||
{
|
||||
if (HasNext)
|
||||
{
|
||||
ChainProxyClient cpc = new ChainProxyClient();
|
||||
var current = this;
|
||||
while (current != null)
|
||||
{
|
||||
switch (current.Type)
|
||||
{
|
||||
case ProxyType.Http:
|
||||
cpc.AddHttpProxy(current.Proxy);
|
||||
break;
|
||||
|
||||
case ProxyType.Socks4:
|
||||
cpc.AddSocks4Proxy(current.Proxy);
|
||||
break;
|
||||
|
||||
case ProxyType.Socks4a:
|
||||
cpc.AddSocks4aProxy(current.Proxy);
|
||||
break;
|
||||
|
||||
case ProxyType.Socks5:
|
||||
cpc.AddSocks5Proxy(current.Proxy);
|
||||
break;
|
||||
}
|
||||
current = current.Next;
|
||||
}
|
||||
return cpc;
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (Type)
|
||||
{
|
||||
case ProxyType.Http:
|
||||
return HttpProxyClient.Parse(Proxy);
|
||||
|
||||
case ProxyType.Socks4:
|
||||
return Socks4ProxyClient.Parse(Proxy);
|
||||
|
||||
case ProxyType.Socks4a:
|
||||
return Socks4aProxyClient.Parse(Proxy);
|
||||
|
||||
case ProxyType.Socks5:
|
||||
return Socks5ProxyClient.Parse(Proxy);
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>The Host string parsed from the proxy.</summary>
|
||||
[BsonIgnore]
|
||||
public string Host
|
||||
{
|
||||
get
|
||||
{
|
||||
try { return Proxy.Split(':').First(); }
|
||||
catch { return ""; }
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>The Port string parsed from the proxy.</summary>
|
||||
[BsonIgnore]
|
||||
public string Port
|
||||
{
|
||||
get
|
||||
{
|
||||
try { return Proxy.Split(':').Last(); }
|
||||
catch { return ""; }
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Whether the proxy is a valid numeric proxy.</summary>
|
||||
[BsonIgnore]
|
||||
public bool IsValidNumeric {
|
||||
get {
|
||||
return !(
|
||||
Host == "" ||
|
||||
Port == "" ||
|
||||
Port.Any(c => !char.IsDigit(c)) ||
|
||||
Host.Split('.').Count() != 4 ||
|
||||
!IsNumeric
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Whether the proxy is numeric.</summary>
|
||||
[BsonIgnore]
|
||||
public bool IsNumeric { get { return !Host.Split('.').Any(x => x.Any(c => !char.IsDigit(c))); } }
|
||||
|
||||
/// <summary>How long ago the proxy was used.</summary>
|
||||
[BsonIgnore]
|
||||
public TimeSpan UsedAgo { get { return DateTime.Now - LastUsed; } }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace RuriLib.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a variable.
|
||||
/// </summary>
|
||||
public class CVar
|
||||
{
|
||||
/// <summary>
|
||||
/// The type of variable.
|
||||
/// </summary>
|
||||
public enum VarType
|
||||
{
|
||||
/// <summary>Holds a single string as value.</summary>
|
||||
Single,
|
||||
|
||||
/// <summary>Holds a list of strings as value.</summary>
|
||||
List,
|
||||
|
||||
/// <summary>Holds a dictionary of strings as value.</summary>
|
||||
Dictionary
|
||||
}
|
||||
|
||||
/// <summary>The variable name.</summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>The dynamic variable value.</summary>
|
||||
public dynamic Value { get; set; }
|
||||
|
||||
/// <summary>Whether the variable is used for the final capture.</summary>
|
||||
public bool IsCapture { get; set; }
|
||||
|
||||
/// <summary>The variable type (it determines which type of value is expected).</summary>
|
||||
public VarType Type { get; set; }
|
||||
|
||||
/// <summary>Whether the variable is hidden and shouldn't be displayed to the user.</summary>
|
||||
public bool Hidden { get; set; }
|
||||
|
||||
// Constructors
|
||||
/// <summary>Needed for NoSQL deserialization.</summary>
|
||||
public CVar() { }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a variable of type Single.
|
||||
/// </summary>
|
||||
/// <param name="name">The variable name</param>
|
||||
/// <param name="value">The variable value as a single string</param>
|
||||
/// <param name="isCapture">Whether the variable is marked as Capture</param>
|
||||
/// <param name="hidden">Whether the variable is hidden</param>
|
||||
public CVar(string name, string value, bool isCapture = false, bool hidden = false)
|
||||
{
|
||||
Name = name;
|
||||
Value = value;
|
||||
IsCapture = isCapture;
|
||||
Type = VarType.Single;
|
||||
Hidden = hidden;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a variable of type List.
|
||||
/// </summary>
|
||||
/// <param name="name">The variable name</param>
|
||||
/// <param name="value">The variable value as a list of strings</param>
|
||||
/// <param name="isCapture">Whether the variable is marked as Capture</param>
|
||||
/// <param name="hidden">Whether the variable is hidden</param>
|
||||
public CVar(string name, List<string> value, bool isCapture = false, bool hidden = false)
|
||||
{
|
||||
Name = name;
|
||||
Value = value;
|
||||
IsCapture = isCapture;
|
||||
Type = VarType.List;
|
||||
Hidden = hidden;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a variable of type Dictionary.
|
||||
/// </summary>
|
||||
/// <param name="name">The variable name</param>
|
||||
/// <param name="value">The variable value as a dictionary of strings</param>
|
||||
/// <param name="isCapture">Whether the variable is marked as Capture</param>
|
||||
/// <param name="hidden">Whether the variable is hidden</param>
|
||||
public CVar(string name, Dictionary<string, string> value, bool isCapture = false, bool hidden = false)
|
||||
{
|
||||
Name = name;
|
||||
Value = value;
|
||||
IsCapture = isCapture;
|
||||
Type = VarType.Dictionary;
|
||||
Hidden = hidden;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a variable of a given type.
|
||||
/// </summary>
|
||||
/// <param name="name">The variable name</param>
|
||||
/// <param name="type">The variable type</param>
|
||||
/// <param name="value">The dynamic variable value</param>
|
||||
/// <param name="isCapture">Whether the variable is marked as Capture</param>
|
||||
/// <param name="hidden">Whether the variable is hidden</param>
|
||||
public CVar(string name, VarType type, dynamic value, bool isCapture = false, bool hidden = false)
|
||||
{
|
||||
Name = name;
|
||||
Value = value;
|
||||
IsCapture = isCapture;
|
||||
Type = type;
|
||||
Hidden = hidden;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Outputs a string from a variable.
|
||||
/// </summary>
|
||||
/// <returns>The formatted value of the value as a single string</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
switch (Type)
|
||||
{
|
||||
case VarType.Single:
|
||||
return Value;
|
||||
|
||||
case VarType.List:
|
||||
return "[" + string.Join(", ", (List<string>)Value) + "]";
|
||||
|
||||
case VarType.Dictionary:
|
||||
return "{" + string.Join(", ", ((Dictionary<string, string>)Value).Select(d => "(" + d.Key + ", " + d.Value + ")")) + "}";
|
||||
|
||||
default:
|
||||
return base.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an item from a List variable.
|
||||
/// </summary>
|
||||
/// <param name="index">The index of the item</param>
|
||||
/// <returns>The item of the List variable's value</returns>
|
||||
public string GetListItem(int index)
|
||||
{
|
||||
if (Type != VarType.List) return null;
|
||||
var list = Value as List<string>;
|
||||
if (index > list.Count - 1 || index < 0) return null;
|
||||
return list[index];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an item from a Dictionary Key.
|
||||
/// </summary>
|
||||
/// <param name="key">The key of the dictionary entry</param>
|
||||
/// <returns>The value of the Dictionary variable's entry</returns>
|
||||
public string GetDictValue(string key)
|
||||
{
|
||||
var dict = Value as Dictionary<string, string>;
|
||||
if (dict.ContainsKey(key)) return dict.First(d => d.Key == key).Value;
|
||||
else return dict.FirstOrDefault(d => d.Key.Contains(key)).Value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an item from a Dictionary Value.
|
||||
/// </summary>
|
||||
/// <param name="value">The value of the dictionary entry</param>
|
||||
/// <returns>The key of the Dictionary variable's entry</returns>
|
||||
public string GetDictKey(string value)
|
||||
{
|
||||
var dict = Value as Dictionary<string, string>;
|
||||
if (dict.ContainsValue(value)) return dict.First(d => d.Value == value).Key;
|
||||
else return dict.FirstOrDefault(d => d.Value.Contains(value)).Key;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
using Newtonsoft.Json;
|
||||
using RuriLib.ViewModels;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using RuriLib.LS;
|
||||
using System.Text.RegularExpressions;
|
||||
using System;
|
||||
|
||||
namespace RuriLib
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a configuration in RuriLib.
|
||||
/// </summary>
|
||||
public class Config : ViewModelBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a Config given the settings and the script.
|
||||
/// </summary>
|
||||
/// <param name="settings">The settings of the Config</param>
|
||||
/// <param name="script">The LoliScript script of the Config</param>
|
||||
public Config(ConfigSettings settings, string script)
|
||||
{
|
||||
Settings = settings;
|
||||
Script = script;
|
||||
}
|
||||
|
||||
/// <summary>The settings of the Config.</summary>
|
||||
public ConfigSettings Settings { get; set; }
|
||||
|
||||
/// <summary>The LoliScript script of the Config.</summary>
|
||||
public string Script { get; set; }
|
||||
|
||||
/// <summary>Whether Selenium is being used in any of the blocks of the Config.</summary>
|
||||
public bool SeleniumPresent { get { return Script.Contains("NAVIGATE") || Script.Contains("BROWSERACTION") || Script.Contains("ELEMENTACTION") || Script.Contains("EXECUTEJS") || Script.Contains("MOUSEACTION"); } }
|
||||
|
||||
/// <summary>Whether a possibly dangerous JavaScript or IronPython script is present in the Config.</summary>
|
||||
public bool DangerousScriptPresent { get { return Script.ToUpper().Contains("BEGIN SCRIPT"); } }
|
||||
|
||||
/// <summary>Whether captcha solving blocks are present in the Config.</summary>
|
||||
public bool CaptchasNeeded { get { return Script.Contains("CAPTCHA"); } }
|
||||
|
||||
/// <summary>Whether bypasscf blocks are present in the Config.</summary>
|
||||
public bool HasCFBypass { get { return Script.Contains("BYPASSCF"); } }
|
||||
|
||||
/// <summary>The amount of blocks of the Config.</summary>
|
||||
public int BlocksAmount { get { return Script.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries).Where(l => BlockParser.IsBlock(l)).Count(); } }
|
||||
|
||||
/// <summary>The joined representation of the allowed WordlistTypes.</summary>
|
||||
public string AllowedWordlists { get { return $"{Settings.AllowedWordlist1} | {Settings.AllowedWordlist2}"; } }
|
||||
|
||||
/// <summary>The pretty representation of the time when the Config was last modified.</summary>
|
||||
public string LastModifiedString { get { return $"{Settings.LastModified.ToShortDateString()} {Settings.LastModified.ToShortTimeString()}"; } }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
using RuriLib.Models;
|
||||
using RuriLib.ViewModels;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
|
||||
namespace RuriLib
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains all the settings of a Config.
|
||||
/// </summary>
|
||||
public class ConfigSettings : ViewModelBase
|
||||
{
|
||||
#region General
|
||||
private string name = "";
|
||||
/// <summary>The name of the Config.</summary>
|
||||
public string Name { get { return name; } set { name = value; OnPropertyChanged(); } }
|
||||
|
||||
private int suggestedBots = 1;
|
||||
/// <summary>The suggested amount of Bots that should be used with the config.</summary>
|
||||
public int SuggestedBots { get { return suggestedBots; } set { suggestedBots = value; OnPropertyChanged(); } }
|
||||
|
||||
private DateTime lastModified = DateTime.Now;
|
||||
/// <summary>When the Config was last modified.</summary>
|
||||
public DateTime LastModified { get { return lastModified; } set { lastModified = value; OnPropertyChanged(); } }
|
||||
|
||||
private string additionalInfo = "";
|
||||
/// <summary>Additional information on the Config usage.</summary>
|
||||
public string AdditionalInfo { get { return additionalInfo; } set { additionalInfo = value; OnPropertyChanged(); } }
|
||||
|
||||
private string author = "";
|
||||
/// <summary>The name of the Author of the Config.</summary>
|
||||
public string Author { get { return author; } set { author = value; OnPropertyChanged(); } }
|
||||
|
||||
private string version = "1.0.0";
|
||||
/// <summary>The version of RuriLib the Config was made with.</summary>
|
||||
public string Version { get { return version; } set { version = value; OnPropertyChanged(); } }
|
||||
#endregion
|
||||
|
||||
#region Requests
|
||||
private bool ignoreResponseErrors = false;
|
||||
/// <summary>Whether to proceed if an HTTP request fails instead of giving the ERROR status.</summary>
|
||||
public bool IgnoreResponseErrors { get { return ignoreResponseErrors; } set { ignoreResponseErrors = value; OnPropertyChanged(); } }
|
||||
#endregion
|
||||
|
||||
#region Proxy
|
||||
private bool needsProxies = false;
|
||||
/// <summary>Whether the Config needs proxies to work (the Runner can override this).</summary>
|
||||
public bool NeedsProxies { get { return needsProxies; } set { needsProxies = value; OnPropertyChanged(); } }
|
||||
|
||||
private bool onlySocks = false;
|
||||
/// <summary>Whether only SOCKS proxies should be used.</summary>
|
||||
public bool OnlySocks { get { return onlySocks; } set { onlySocks = value; OnPropertyChanged(); } }
|
||||
|
||||
private bool onlySsl = false;
|
||||
/// <summary>Whether only SSL proxies should be used.</summary>
|
||||
public bool OnlySsl { get { return onlySsl; } set { onlySsl = value; OnPropertyChanged(); } }
|
||||
|
||||
private int maxProxyUses = 0;
|
||||
/// <summary>The maximum amount of times a proxy can be used before being banned by the Runner (0 for infinite).</summary>
|
||||
public int MaxProxyUses { get { return maxProxyUses; } set { maxProxyUses = value; OnPropertyChanged(); } }
|
||||
#endregion
|
||||
|
||||
#region Data
|
||||
private string allowedWordlist1 = "";
|
||||
/// <summary>The name of the first allowed WordlistType.</summary>
|
||||
public string AllowedWordlist1 { get { return allowedWordlist1; } set { allowedWordlist1 = value; OnPropertyChanged(); } }
|
||||
|
||||
private string allowedWordlist2 = "";
|
||||
/// <summary>The name of the second allowed WordlistType.</summary>
|
||||
public string AllowedWordlist2 { get { return allowedWordlist2; } set { allowedWordlist2 = value; OnPropertyChanged(); } }
|
||||
|
||||
/// <summary>The collection of data rules that check if a data line is valid.</summary>
|
||||
public ObservableCollection<DataRule> DataRules { get; set; } = new ObservableCollection<DataRule>();
|
||||
#endregion
|
||||
|
||||
#region Inputs
|
||||
/// <summary>The collection of custom inputs that the Runner asks to the user upon start.</summary>
|
||||
public ObservableCollection<CustomInput> CustomInputs { get; set; } = new ObservableCollection<CustomInput>();
|
||||
#endregion
|
||||
|
||||
#region Selenium
|
||||
private bool forceHeadless = false;
|
||||
/// <summary>Whether to override the default choice of the user and force headless mode.</summary>
|
||||
public bool ForceHeadless { get { return forceHeadless; } set { forceHeadless = value; OnPropertyChanged(); } }
|
||||
|
||||
private bool alwaysOpen = false;
|
||||
/// <summary>Whether to always open a browser at the start of the checking process (if not already open).</summary>
|
||||
public bool AlwaysOpen { get { return alwaysOpen; } set { alwaysOpen = value; OnPropertyChanged(); } }
|
||||
|
||||
private bool alwaysQuit = false;
|
||||
/// <summary>Whether to always quit the browser and dispose of the WebDriver at the end of the checking process (if any browser is open).</summary>
|
||||
public bool AlwaysQuit { get { return alwaysQuit; } set { alwaysQuit = value; OnPropertyChanged(); } }
|
||||
|
||||
private bool disableNotifications = false;
|
||||
/// <summary>Whether to disable notifications the lock the page and make it impossible to proceed.</summary>
|
||||
public bool DisableNotifications { get { return disableNotifications; } set { disableNotifications = value; OnPropertyChanged(); } }
|
||||
|
||||
private string customUserAgent = "";
|
||||
/// <summary>The custom User-Agent header that should be used for every request of the browser.</summary>
|
||||
public string CustomUserAgent { get { return customUserAgent; } set { customUserAgent = value; OnPropertyChanged(); } }
|
||||
|
||||
private bool randomUA = false;
|
||||
/// <summary>Whether to choose a random User-Agent header when opening a browser instance.</summary>
|
||||
public bool RandomUA { get { return randomUA; } set { randomUA = value; OnPropertyChanged(); } }
|
||||
|
||||
private string customCMDArgs = "";
|
||||
/// <summary>The custom command line arguments that are sent when running the executable file.</summary>
|
||||
public string CustomCMDArgs { get { return customCMDArgs; } set { customCMDArgs = value; OnPropertyChanged(); } }
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
/// <summary>
|
||||
/// Removes a DataRule given its id.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique id of the DataRule</param>
|
||||
public void RemoveDataRuleById(int id)
|
||||
{
|
||||
DataRules.Remove(GetDataRuleById(id));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a DataRule given its id.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique id of the DataRule</param>
|
||||
/// <returns>The DataRule</returns>
|
||||
public DataRule GetDataRuleById(int id)
|
||||
{
|
||||
return DataRules.FirstOrDefault(r => r.Id == id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a CustomInput given its id.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique id of the CustomInput</param>
|
||||
public void RemoveCustomInputById(int id)
|
||||
{
|
||||
CustomInputs.Remove(GetCustomInputById(id));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a CustomInput given its id.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique id of the CustomInput</param>
|
||||
/// <returns>The CustomInput</returns>
|
||||
public CustomInput GetCustomInputById(int id)
|
||||
{
|
||||
return CustomInputs.FirstOrDefault(c => c.Id == id);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
|
||||
namespace RuriLib.ViewModels
|
||||
{
|
||||
/// <summary>
|
||||
/// A custom input that is required to the user before the Config is started.
|
||||
/// </summary>
|
||||
public class CustomInput : ViewModelBase
|
||||
{
|
||||
private string description = "";
|
||||
/// <summary>The description of what the user needs to enter.</summary>
|
||||
public string Description { get { return description; } set { description = value; OnPropertyChanged(); } }
|
||||
|
||||
private string variableName = "";
|
||||
/// <summary>The name of the variable to create basing on the value provided by the user.</summary>
|
||||
public string VariableName { get { return variableName; } set { variableName = value; OnPropertyChanged(); } }
|
||||
|
||||
/// <summary>The id of the Custom Input.</summary>
|
||||
public int Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a CustomInput given an id.
|
||||
/// </summary>
|
||||
/// <param name="id">A unique id</param>
|
||||
public CustomInput(int id)
|
||||
{
|
||||
Id = id;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace RuriLib.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// A KeyChain that can be customized in name and color.
|
||||
/// </summary>
|
||||
public class CustomKeychain
|
||||
{
|
||||
/// <summary>The name of the KeyChain.</summary>
|
||||
public string Name { get; set; } = "CUSTOM";
|
||||
|
||||
/// <summary>The color of the KeyChain.</summary>
|
||||
public Color Color { get; set; } = Colors.OrangeRed;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
using RuriLib.ViewModels;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace RuriLib.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// The pool where data lines are taken from.
|
||||
/// </summary>
|
||||
public class DataPool : ViewModelBase
|
||||
{
|
||||
/// <summary>The IEnumerable of all available data lines.</summary>
|
||||
public IEnumerable<string> List { get; }
|
||||
|
||||
/// <summary>The total number of lines.</summary>
|
||||
public int Size { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a DataPool given an IEnumerable and counts the amount of lines.
|
||||
/// </summary>
|
||||
/// <param name="list">The IEnumerable to pick lines from</param>
|
||||
public DataPool(IEnumerable<string> list)
|
||||
{
|
||||
List = list;
|
||||
Size = List.Count();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a DataPool by loading lines from a file.
|
||||
/// </summary>
|
||||
/// <param name="fileName">The name of the file to load data lines from</param>
|
||||
public DataPool(string fileName)
|
||||
{
|
||||
List = File.ReadLines(fileName);
|
||||
Size = List.Count();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a DataPool by generating all the possible combinations of a string.
|
||||
/// </summary>
|
||||
/// <param name="charSet">The allowed character set (one after the other like in the string "abcdef")</param>
|
||||
/// <param name="length">The length of the output combinations</param>
|
||||
public DataPool(string charSet, int length)
|
||||
{
|
||||
List = charSet.Select(x => x.ToString());
|
||||
for (int i = 0; i < length; i++)
|
||||
List = List.SelectMany(x => charSet, (x, y) => x + y);
|
||||
Size = (int)Math.Pow(charSet.Length, length);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
using Newtonsoft.Json;
|
||||
using RuriLib.ViewModels;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace RuriLib.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// The condition for which a Rule is tested.
|
||||
/// </summary>
|
||||
public enum RuleType
|
||||
{
|
||||
/// <summary>The slice must contain the given characters.</summary>
|
||||
MustContain,
|
||||
|
||||
/// <summary>The slice must not contain the given characters.</summary>
|
||||
MustNotContain,
|
||||
|
||||
/// <summary>The slice's length must be greater or equal to a given number.</summary>
|
||||
MinLength,
|
||||
|
||||
/// <summary>The slice's length must be smaller or equal to a given number.</summary>
|
||||
MaxLength
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a rule that the data line must respect in order to be valid for a given Config.
|
||||
/// </summary>
|
||||
public class DataRule : ViewModelBase
|
||||
{
|
||||
private string sliceName = "";
|
||||
/// <summary>The name of the specific slice (defined in the WordlistType) this rule refers to.</summary>
|
||||
public string SliceName { get { return sliceName; } set { sliceName = value; OnPropertyChanged(); } }
|
||||
|
||||
private RuleType ruleType = RuleType.MustContain;
|
||||
/// <summary>The type of the rule.</summary>
|
||||
public RuleType RuleType { get { return ruleType; } set { ruleType = value; OnPropertyChanged(); } }
|
||||
|
||||
private string ruleString = "Lowercase";
|
||||
/// <summary>The characters to search in the sliced value. Defaults are Lowercase, Uppercase, Digit, Symbol. Custom character sets can be created by concatenating the characters in a single string (e.g. ABCDEF).</summary>
|
||||
public string RuleString { get { return ruleString; } set { ruleString = value; OnPropertyChanged(); } }
|
||||
|
||||
/// <summary>The id of the rule.</summary>
|
||||
public int Id { get; set; }
|
||||
|
||||
/// <summary>Whether the type has been initialized. Used in the View.</summary>
|
||||
[JsonIgnore]
|
||||
public bool TypeInitialized { get; set; } = false;
|
||||
|
||||
/// <summary>Whether the right-hand term has been initialized. Used in the View.</summary>
|
||||
[JsonIgnore]
|
||||
public bool StringInitialized { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a DataRule given an id.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique id of the rule</param>
|
||||
public DataRule(int id)
|
||||
{
|
||||
Id = id;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace RuriLib.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Settings for customizeable data types used in the library.
|
||||
/// </summary>
|
||||
public class EnvironmentSettings
|
||||
{
|
||||
/// <summary>List of custom Wordlist Types.</summary>
|
||||
public List<WordlistType> WordlistTypes { get; set; } = new List<WordlistType>();
|
||||
|
||||
/// <summary>List of custom KeyChain Types.</summary>
|
||||
public List<CustomKeychain> CustomKeychains { get; set; } = new List<CustomKeychain>();
|
||||
|
||||
/// <summary>List of custom Export Formats.</summary>
|
||||
public List<ExportFormat> ExportFormats { get; set; } = new List<ExportFormat>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the names of all Wordlist Types.
|
||||
/// </summary>
|
||||
/// <returns>The list of all the names of custom Wordlist Types</returns>
|
||||
public List<string> GetWordlistTypeNames()
|
||||
{
|
||||
return WordlistTypes.Select(w => w.Name).ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the names of all Custom KeyChains.
|
||||
/// </summary>
|
||||
/// <returns>The list of all the names of custom KeyChain types</returns>
|
||||
public List<string> GetCustomKeychainNames()
|
||||
{
|
||||
return CustomKeychains.Select(c => c.Name).ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a Custom KeyChain given its name.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the Custom KeyChain</param>
|
||||
/// <returns>The CustomKeychain object if found, a default one if not</returns>
|
||||
public CustomKeychain GetCustomKeychain(string name)
|
||||
{
|
||||
try { return CustomKeychains.First(w => w.Name == name); }
|
||||
catch { return new CustomKeychain(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a Wordlist Type given its name.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the Wordlist Type</param>
|
||||
/// <returns>The WordlistType object if found, a default one if not</returns>
|
||||
public WordlistType GetWordlistType(string name)
|
||||
{
|
||||
try { return WordlistTypes.FirstOrDefault(w => w.Name == name); }
|
||||
catch { return new WordlistType(); }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace RuriLib.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// The export format for saving a Hit as a formatted string.
|
||||
/// </summary>
|
||||
public class ExportFormat
|
||||
{
|
||||
/// <summary>The format of the desired string.</summary>
|
||||
public string Format { get; set; } = "<DATA>";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace RuriLib.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the outcome of a successful check, used as Data Type in a Database.
|
||||
/// </summary>
|
||||
public class Hit
|
||||
{
|
||||
/// <summary>Needed for NoSQL storage.</summary>
|
||||
public Guid Id { get; set; }
|
||||
|
||||
/// <summary>The data line that was used.</summary>
|
||||
public string Data { get; set; }
|
||||
|
||||
/// <summary>The list of all variables marked as Capture.</summary>
|
||||
public VariableList CapturedData { get; set; }
|
||||
|
||||
/// <summary>The list of all variables marked as Capture as a chained string.</summary>
|
||||
[JsonIgnore]
|
||||
public string CapturedString { get { return CapturedData.ToCaptureString(); } }
|
||||
|
||||
/// <summary>The proxy that was used.</summary>
|
||||
public string Proxy { get; set; }
|
||||
|
||||
/// <summary>The timestamp of when the Hit was found.</summary>
|
||||
public DateTime Date { get; set; }
|
||||
|
||||
/// <summary>The type of Hit.</summary>
|
||||
public string Type { get; set; }
|
||||
|
||||
/// <summary>The name of the Config that was used.</summary>
|
||||
public string ConfigName { get; set; }
|
||||
|
||||
/// <summary>The name of the Wordlist that was used.</summary>
|
||||
public string WordlistName { get; set; }
|
||||
|
||||
/// <summary>Needed for NoSQL deserialization.</summary>
|
||||
public Hit() { }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a Hit given its details.
|
||||
/// </summary>
|
||||
/// <param name="data">The data line used</param>
|
||||
/// <param name="capturedData">The VariableList of all captured data</param>
|
||||
/// <param name="proxy">The proxy used</param>
|
||||
/// <param name="type">The type of the Hit</param>
|
||||
/// <param name="configName">The Config's name</param>
|
||||
/// <param name="wordlistName">The Wordlist's name</param>
|
||||
public Hit(string data, VariableList capturedData, string proxy, string type, string configName, string wordlistName)
|
||||
{
|
||||
Data = data;
|
||||
CapturedData = capturedData;
|
||||
Proxy = proxy;
|
||||
Date = DateTime.Now;
|
||||
Type = type;
|
||||
ConfigName = configName;
|
||||
WordlistName = wordlistName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Outputs the Hit details in a given format.
|
||||
/// </summary>
|
||||
/// <param name="format">The format in which variables should be replaced</param>
|
||||
/// <returns>The formatted string with all variables replaced</returns>
|
||||
public string ToFormattedString(string format)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder(format)
|
||||
.Replace("<DATA>", Data)
|
||||
.Replace("<PROXY>", Proxy)
|
||||
.Replace("<DATE>", Date.ToLongDateString() + " " + Date.ToLongTimeString())
|
||||
.Replace("<CONFIG>", ConfigName)
|
||||
.Replace("<WORDLIST>", WordlistName)
|
||||
.Replace("<TYPE>", Type)
|
||||
.Replace("<CAPTURE>", CapturedData.ToCaptureString());
|
||||
|
||||
foreach(var cap in CapturedData.All)
|
||||
sb.Replace("<" + cap.Name + ">", cap.ToString());
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using RuriLib.ViewModels;
|
||||
|
||||
namespace RuriLib.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a Key in a KeyChain.
|
||||
/// </summary>
|
||||
public class Key : ViewModelBase
|
||||
{
|
||||
private string leftTerm = "<SOURCE>";
|
||||
/// <summary>The left-hand term for the comparison.</summary>
|
||||
public string LeftTerm { get { return leftTerm; } set { leftTerm = value; OnPropertyChanged(); } }
|
||||
|
||||
private Condition condition = Condition.Contains;
|
||||
/// <summary>The condition of the comparison.</summary>
|
||||
public Condition Condition { get { return condition; } set { condition = value; OnPropertyChanged(); } }
|
||||
|
||||
private string rightTerm = "";
|
||||
/// <summary>The right-hand term of the comparison.</summary>
|
||||
public string RightTerm { get { return rightTerm; } set { rightTerm = value; OnPropertyChanged(); } }
|
||||
|
||||
/// <summary>
|
||||
/// Checks the comparison between left and right member.
|
||||
/// </summary>
|
||||
/// <param name="data">The BotData used for variable replacement.</param>
|
||||
/// <returns>Whether the comparison is valid</returns>
|
||||
public bool CheckKey(BotData data)
|
||||
{
|
||||
return ConditionChecker.Verify(LeftTerm, Condition, RightTerm, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
using RuriLib.Models;
|
||||
using RuriLib.ViewModels;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace RuriLib.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a set of keys that can be checked in a OR/AND fashion and modifies the status of the BotData if successful.
|
||||
/// </summary>
|
||||
public class KeyChain : ViewModelBase
|
||||
{
|
||||
/// <summary>The returned status upon a successful check of the keys. If no KeyChain was valid, the original status won't be changed.</summary>
|
||||
public enum KeychainType
|
||||
{
|
||||
/// <summary>Sets a SUCCESS status in the bot.</summary>
|
||||
Success,
|
||||
|
||||
/// <summary>Sets a FAIL status in the bot.</summary>
|
||||
Failure,
|
||||
|
||||
/// <summary>Sets a BAN status in the bot.</summary>
|
||||
Ban,
|
||||
|
||||
/// <summary>Sets a RETRY status in the bot.</summary>
|
||||
Retry,
|
||||
|
||||
/// <summary>Sets a CUSTOM status in the bot.</summary>
|
||||
Custom
|
||||
}
|
||||
|
||||
/// <summary>The mode in which the keys should be checked.</summary>
|
||||
public enum KeychainMode
|
||||
{
|
||||
/// <summary>Trigger the KeyChain if ANY Key is valid.</summary>
|
||||
OR,
|
||||
|
||||
/// <summary>Trigger the KeyChain if ALL the Keys are valid.</summary>
|
||||
AND
|
||||
}
|
||||
|
||||
private KeychainType type = KeychainType.Success;
|
||||
/// <summary>The type of the KeyChain.</summary>
|
||||
public KeychainType Type { get { return type; } set { type = value; OnPropertyChanged(); } }
|
||||
|
||||
private KeychainMode mode = KeychainMode.OR;
|
||||
/// <summary>The mode of the KeyChain.</summary>
|
||||
public KeychainMode Mode { get { return mode; } set { mode = value; OnPropertyChanged(); } }
|
||||
|
||||
private string customType = "CUSTOM";
|
||||
/// <summary>The type of the KeyChain in case Custom is selected.</summary>
|
||||
public string CustomType { get { return customType; } set { customType = value; OnPropertyChanged(); } }
|
||||
|
||||
/// <summary>The collection of Keys in the KeyChain.</summary>
|
||||
public ObservableCollection<Key> Keys { get; set; } = new ObservableCollection<Key>();
|
||||
|
||||
/// <summary>
|
||||
/// Checks all the Keys in the KeyChain.
|
||||
/// </summary>
|
||||
/// <param name="data">The BotData used for variable replacement</param>
|
||||
/// <returns>Whether the KeyChain was triggered or not</returns>
|
||||
public bool CheckKeys(BotData data)
|
||||
{
|
||||
switch (Mode)
|
||||
{
|
||||
case KeychainMode.OR:
|
||||
foreach (var key in Keys)
|
||||
{
|
||||
if (key.CheckKey(data))
|
||||
{
|
||||
data.Log(new LogEntry(string.Format("Found 'OR' Key {0} {1} {2}",
|
||||
BlockBase.TruncatePretty(BlockBase.ReplaceValues(key.LeftTerm, data), 20),
|
||||
key.Condition.ToString(),
|
||||
BlockBase.ReplaceValues(key.RightTerm, data)),
|
||||
Colors.White));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
case KeychainMode.AND:
|
||||
foreach (var key in Keys)
|
||||
{
|
||||
if (!key.CheckKey(data))
|
||||
return false;
|
||||
else
|
||||
{
|
||||
data.Log(new LogEntry(string.Format("Found 'AND' Key {0} {1} {2}",
|
||||
BlockBase.TruncatePretty(BlockBase.ReplaceValues(key.LeftTerm, data), 20),
|
||||
key.Condition.ToString(),
|
||||
BlockBase.ReplaceValues(key.RightTerm, data)),
|
||||
Colors.White));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
using RuriLib.ViewModels;
|
||||
using System;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace RuriLib
|
||||
{
|
||||
/// <summary>
|
||||
/// How critical a log entry is.
|
||||
/// </summary>
|
||||
public enum LogLevel
|
||||
{
|
||||
/// <summary>An informational message.</summary>
|
||||
Info,
|
||||
|
||||
/// <summary>A warning message.</summary>
|
||||
Warning,
|
||||
|
||||
/// <summary>An error message.</summary>
|
||||
Error
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A single entry in the log of the application.
|
||||
/// </summary>
|
||||
public class LogEntry : ViewModelBase
|
||||
{
|
||||
private string logString = "";
|
||||
/// <summary>The logged message.</summary>
|
||||
public string LogString { get { return logString; } set { logString = value; OnPropertyChanged(); } }
|
||||
|
||||
private Color logColor = Colors.White;
|
||||
/// <summary>The color of the logged entry.</summary>
|
||||
public Color LogColor { get { return logColor; } set { logColor = value; OnPropertyChanged(); } }
|
||||
|
||||
private DateTime logTime = DateTime.Now;
|
||||
/// <summary>The timestamp of the logged entry.</summary>
|
||||
public DateTime LogTime { get { return logTime; } set { logTime = value; OnPropertyChanged(); } }
|
||||
|
||||
private LogLevel logLevel = LogLevel.Info;
|
||||
/// <summary>The level of the logged entry.</summary>
|
||||
public LogLevel LogLevel { get { return logLevel; } set { logLevel = value; OnPropertyChanged(); } }
|
||||
|
||||
private string logComponent = "";
|
||||
/// <summary>The component that logged the entry.</summary>
|
||||
public string LogComponent { get { return logComponent; } set { logComponent = value; OnPropertyChanged(); } }
|
||||
|
||||
/// <summary>
|
||||
/// <para>Creates a Log Entry given a message and a color.</para>
|
||||
/// <para>This constructor is used to generate entries for the Bot Log.</para>
|
||||
/// <para>The level is Info and the component is an empty string.</para>
|
||||
/// </summary>
|
||||
/// <param name="logString">The message to log</param>
|
||||
/// <param name="logColor">The color of the message</param>
|
||||
public LogEntry(string logString, Color logColor)
|
||||
{
|
||||
LogString = logString;
|
||||
LogColor = logColor;
|
||||
LogTime = DateTime.Now;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>Creates a Log Entry given a component, a message and a level.</para>
|
||||
/// <para>This constructor is used for logging messages coming from components of the View.</para>
|
||||
/// </summary>
|
||||
/// <param name="logComponent">The Component that is logging the entry</param>
|
||||
/// <param name="logString">The message to log</param>
|
||||
/// <param name="logLevel">The level of the log</param>
|
||||
public LogEntry(string logComponent, string logString, LogLevel logLevel)
|
||||
{
|
||||
LogString = logString;
|
||||
LogTime = DateTime.Now;
|
||||
LogLevel = logLevel;
|
||||
LogComponent = logComponent;
|
||||
|
||||
switch (LogLevel)
|
||||
{
|
||||
case LogLevel.Info:
|
||||
LogColor = Colors.White;
|
||||
break;
|
||||
|
||||
case LogLevel.Warning:
|
||||
LogColor = Colors.Gold;
|
||||
break;
|
||||
|
||||
case LogLevel.Error:
|
||||
LogColor = Colors.Tomato;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
|
||||
namespace RuriLib.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Class that manages a pool of proxies.
|
||||
/// </summary>
|
||||
public class ProxyPool
|
||||
{
|
||||
/// <summary>The full list of proxies in the pool.</summary>
|
||||
public List<CProxy> Proxies { get; } = new List<CProxy>();
|
||||
|
||||
/// <summary>The list of Alive proxies (AVAILABLE or BUSY).</summary>
|
||||
public List<CProxy> Alive { get { return Proxies.Where(p => p.Status == Status.AVAILABLE || p.Status == Status.BUSY).ToList(); } }
|
||||
|
||||
/// <summary>The list of Available proxies.</summary>
|
||||
public List<CProxy> Available { get { return Proxies.Where(p => p.Status == Status.AVAILABLE).ToList(); } }
|
||||
|
||||
/// <summary>The list of Banned proxies.</summary>
|
||||
public List<CProxy> Banned { get { return Proxies.Where(p => p.Status == Status.BANNED).ToList(); } }
|
||||
|
||||
/// <summary>The list of Bad proxies.</summary>
|
||||
public List<CProxy> Bad { get { return Proxies.Where(p => p.Status == Status.BAD).ToList(); } }
|
||||
|
||||
/// <summary>
|
||||
/// <para>Whether the proxy list is locked or not.</para>
|
||||
/// <para>It prevents multiple bots from choosing the same proxy simultaneously, regardless of the fact that concurrent usage may be disabled.</para>
|
||||
/// </summary>
|
||||
private bool Locked { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the proxy pool given a collection of string to be parsed as proxies and their type.
|
||||
/// </summary>
|
||||
/// <param name="proxies">The collection of strings to parse the proxies from</param>
|
||||
/// <param name="type">The type of the proxies</param>
|
||||
public ProxyPool(IEnumerable<string> proxies, Extreme.Net.ProxyType type)
|
||||
{
|
||||
Proxies = proxies.Select(p => new CProxy(p, type)).ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>Initializes the proxy pool given a collection of CProxy objects.</para>
|
||||
/// <para>The proxies will be first cloned and then stored in the list.</para>
|
||||
/// <para>They will also be unbanned to make sure there are no leftovers from previous checks.</para>
|
||||
/// </summary>
|
||||
/// <param name="proxies">The list of CProxy objects to be cloned and added to the list</param>
|
||||
public ProxyPool(List<CProxy> proxies)
|
||||
{
|
||||
// We clone the list, since we don't want to target to the same objects that are in the Proxy Manager
|
||||
// If we don't do this, a ban on a runner instance would cause a ban on all the other instances as well!
|
||||
Proxies = IOManager.CloneProxies(proxies);
|
||||
|
||||
// Now that the list is cloned, we make sure that no proxy is already banned
|
||||
UnbanAll();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the Clearance and Cfduid cookies set by Cloudflare.
|
||||
/// </summary>
|
||||
public void ClearCF()
|
||||
{
|
||||
foreach (var proxy in Proxies)
|
||||
{
|
||||
proxy.Clearance = "";
|
||||
proxy.Cfduid = "";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets all the BANNED and BAD proxies status to AVAILABLE and resets their Uses. It also clears the Cloudflare cookies.
|
||||
/// </summary>
|
||||
public void UnbanAll()
|
||||
{
|
||||
foreach (var proxy in Proxies)
|
||||
{
|
||||
if (proxy.Status == Status.BANNED || proxy.Status == Status.BAD)
|
||||
{
|
||||
proxy.Status = Status.AVAILABLE;
|
||||
proxy.Hooked = 0;
|
||||
proxy.Uses = 0;
|
||||
}
|
||||
}
|
||||
ClearCF();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to return the first available proxy from the list.
|
||||
/// </summary>
|
||||
/// <param name="evenBusy">Whether to include proxies that are being used by other bots</param>
|
||||
/// <param name="maxUses">The maximum uses of a proxy, after which it will be banned (0 for infinite)</param>
|
||||
/// <param name="neverBan">Whether a proxy can ever be banned</param>
|
||||
/// <returns>The first available proxy respecting the conditions or null</returns>
|
||||
public CProxy GetProxy(bool evenBusy, int maxUses, bool neverBan = false)
|
||||
{
|
||||
while (Locked) Thread.Sleep(10);
|
||||
|
||||
CProxy proxy;
|
||||
Locked = true;
|
||||
if (evenBusy)
|
||||
{
|
||||
if (maxUses == 0) proxy = Alive.FirstOrDefault();
|
||||
else proxy = Alive.Where(p => p.Uses < maxUses).FirstOrDefault();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (maxUses == 0) proxy = Available.FirstOrDefault();
|
||||
else proxy = Available.Where(p => p.Uses < maxUses).FirstOrDefault();
|
||||
}
|
||||
|
||||
if (proxy != null)
|
||||
{
|
||||
if (maxUses > 0 && proxy.Uses > maxUses && !neverBan)
|
||||
{
|
||||
proxy.Status = Status.BANNED;
|
||||
Locked = false;
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
proxy.Status = Status.BUSY;
|
||||
proxy.Hooked++;
|
||||
}
|
||||
}
|
||||
Locked = false;
|
||||
return proxy;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
using System;
|
||||
|
||||
namespace RuriLib.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// A record that, given a Config's name and a Wordlist's location, returns how many data lines have already been checked.
|
||||
/// </summary>
|
||||
public class Record
|
||||
{
|
||||
/// <summary>Needed for NoSQL storage.</summary>
|
||||
public Guid Id { get; set; }
|
||||
|
||||
/// <summary>The name of the Config.</summary>
|
||||
public string ConfigName { get; set; }
|
||||
|
||||
/// <summary>The location on disk of the Wordlist.</summary>
|
||||
public string WordlistLocation { get; set; }
|
||||
|
||||
/// <summary>How many data lines were already checked.</summary>
|
||||
public int Checkpoint { get; set; }
|
||||
|
||||
/// <summary>Needed for NoSQL deserialization.</summary>
|
||||
public Record() { }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a Record to be stored in the Database.
|
||||
/// </summary>
|
||||
/// <param name="configName">The name of the Config used in the check</param>
|
||||
/// <param name="wordlistLocation">The location on disk of the Wordlist used in the check</param>
|
||||
/// <param name="checkpoint">The amount of data lines checked when creating the record</param>
|
||||
public Record(string configName, string wordlistLocation, int checkpoint)
|
||||
{
|
||||
ConfigName = configName;
|
||||
WordlistLocation = wordlistLocation;
|
||||
Checkpoint = checkpoint;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
using RuriLib.ViewModels;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace RuriLib.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the outcome of a check which can be represented in a list and can give information on the details of the check.
|
||||
/// </summary>
|
||||
public class ValidData : ViewModelBase
|
||||
{
|
||||
private string data;
|
||||
/// <summary>The data line that was checked.</summary>
|
||||
public string Data { get { return data; } set { data = value; OnPropertyChanged(); } }
|
||||
|
||||
private string proxy;
|
||||
/// <summary>The proxy that was used for the check.</summary>
|
||||
public string Proxy { get { return proxy; } set { proxy = value; OnPropertyChanged(); } }
|
||||
|
||||
private BotStatus result;
|
||||
/// <summary>The result of the check.</summary>
|
||||
public BotStatus Result { get { return result; } set { result = value; OnPropertyChanged(); } }
|
||||
|
||||
private string capturedData;
|
||||
/// <summary>The data captured during the check.</summary>
|
||||
public string CapturedData { get { return capturedData; } set { capturedData = value; OnPropertyChanged(); } }
|
||||
|
||||
private int unixDate;
|
||||
/// <summary>The unix timestamp of the check completion.</summary>
|
||||
public int UnixDate { get { return unixDate; } set { unixDate = value; OnPropertyChanged(); OnPropertyChanged(); } }
|
||||
|
||||
/// <summary>The timestamp of the check completion as a formatted date.</summary>
|
||||
public string Timestamp { get { return (new DateTime(1970, 1, 1)).AddSeconds(UnixDate).ToShortDateString(); } }
|
||||
|
||||
/// <summary>The timestamp of the check completion as a DateTime object.</summary>
|
||||
public DateTime Time { get { return (new DateTime(1970, 1, 1)).AddSeconds(UnixDate); } }
|
||||
|
||||
private string source;
|
||||
/// <summary>The contents of the last page's source code at the end of the check.</summary>
|
||||
public string Source { get { return source; } set { source = value; OnPropertyChanged(); } }
|
||||
|
||||
private List<LogEntry> log;
|
||||
/// <summary>The entire log with the details of the check.</summary>
|
||||
public List<LogEntry> Log { get { return log; } set { log = value; OnPropertyChanged(); } }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a ValidData object after a valid check.
|
||||
/// </summary>
|
||||
/// <param name="data">The data line that was used in the check</param>
|
||||
/// <param name="proxy">The proxy that was used for the check (empty string if none)</param>
|
||||
/// <param name="result">The result of the check</param>
|
||||
/// <param name="capturedData">The data captured during the check</param>
|
||||
/// <param name="source">The last page source code of the check</param>
|
||||
/// <param name="log">The detailed log of the check</param>
|
||||
public ValidData(string data, string proxy, BotStatus result, string capturedData, string source, List<LogEntry> log)
|
||||
{
|
||||
Data = data;
|
||||
Proxy = proxy;
|
||||
Result = result;
|
||||
CapturedData = capturedData;
|
||||
UnixDate = (int)Math.Round((DateTime.Now - new DateTime(1970, 1, 1, 0, 0, 0)).TotalSeconds);
|
||||
Source = source;
|
||||
Log = log;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
using Newtonsoft.Json;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace RuriLib.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Class that allows to easily manage a list of CVar objects.
|
||||
/// </summary>
|
||||
public class VariableList
|
||||
{
|
||||
/// <summary>The whole list of variables.</summary>
|
||||
public List<CVar> All { get; set; }
|
||||
|
||||
/// <summary>The list of all variables marked as Capture.</summary>
|
||||
[JsonIgnore]
|
||||
public List<CVar> Captures { get { return All.Where(v => v.IsCapture && !v.Hidden).ToList(); } }
|
||||
|
||||
/// <summary>The list of all variables of type Single.</summary>
|
||||
[JsonIgnore]
|
||||
public List<CVar> Singles { get { return All.Where(v => v.Type == CVar.VarType.Single).ToList(); } }
|
||||
|
||||
/// <summary>The list of all variables of type List.</summary>
|
||||
[JsonIgnore]
|
||||
public List<CVar> Lists { get { return All.Where(v => v.Type == CVar.VarType.List).ToList(); } }
|
||||
|
||||
/// <summary>The list of all variables of type Dictionary.</summary>
|
||||
[JsonIgnore]
|
||||
public List<CVar> Dictionaries { get { return All.Where(v => v.Type == CVar.VarType.Dictionary).ToList(); } }
|
||||
|
||||
/// <summary>
|
||||
/// Standard constructor that initializes an empty variable list.
|
||||
/// </summary>
|
||||
public VariableList()
|
||||
{
|
||||
All = new List<CVar>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a variable list starting from an existing list of variables.
|
||||
/// </summary>
|
||||
/// <param name="list">The list of variables.</param>
|
||||
public VariableList(List<CVar> list)
|
||||
{
|
||||
All = list;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a variable given its name.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the variable</param>
|
||||
/// <returns>The variable or null if it wasn't found.</returns>
|
||||
public CVar Get(string name)
|
||||
{
|
||||
return All.FirstOrDefault(v => v.Name == name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a variable given its name and type.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the variable</param>
|
||||
/// <param name="type">The type of the variable</param>
|
||||
/// <returns>The variable or null if it wasn't found.</returns>
|
||||
public CVar Get(string name, CVar.VarType type)
|
||||
{
|
||||
return All.FirstOrDefault(v => v.Name == name && v.Type == type);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper method that gets the value of a variable of type Single.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the Single variable</param>
|
||||
/// <returns>The string value or null if it wasn't found.</returns>
|
||||
public string GetSingle(string name)
|
||||
{
|
||||
try { return (string)Get(name, CVar.VarType.Single).Value; }
|
||||
catch { return null; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper method that gets the value of a variable of type List.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the List variable</param>
|
||||
/// <returns>The list of strings value or null if it wasn't found.</returns>
|
||||
public List<string> GetList(string name)
|
||||
{
|
||||
try { return (List<string>)Get(name, CVar.VarType.List).Value; }
|
||||
catch { return null; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper method that gets the value of a variable of type Dictionary.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the Dictionary variable</param>
|
||||
/// <returns>The dictionary value or null if it wasn't found.</returns>
|
||||
public Dictionary<string,string> GetDictionary(string name)
|
||||
{
|
||||
try { return (Dictionary<string, string>)Get(name, CVar.VarType.Dictionary).Value; }
|
||||
catch { return null; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper method that checks if a variable exists given its name.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the variable</param>
|
||||
/// <returns>True if the variable exists</returns>
|
||||
public bool VariableExists(string name)
|
||||
{
|
||||
return All.Any(v => v.Name == name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper method that checks if a variable exists given its name and type.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the variable</param>
|
||||
/// <param name="type">The type of the variable</param>
|
||||
/// <returns>True if the variable exists and matches the given type</returns>
|
||||
public bool VariableExists(string name, CVar.VarType type)
|
||||
{
|
||||
return All.Any(v => v.Name == name && v.Type == type);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a CVar object to the variable list.
|
||||
/// </summary>
|
||||
/// <param name="variable">The CVar object to add</param>
|
||||
public void Set(CVar variable)
|
||||
{
|
||||
// First of all remove any old variable with the same name
|
||||
Remove(variable.Name);
|
||||
|
||||
// Then add the new one
|
||||
All.Add(variable);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a hidden CVar object to the variable list.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the variable to add</param>
|
||||
/// <param name="value">The value of the variable to add</param>
|
||||
public void SetHidden(string name, dynamic value)
|
||||
{
|
||||
if (All.Any(v => v.Name == name && v.Hidden))
|
||||
All.FirstOrDefault(v => v.Name == name && v.Hidden).Value = value;
|
||||
else
|
||||
All.Add(new CVar(name, value, false, true));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a CVar object to the variable list only if no other variable with the same name exists.
|
||||
/// </summary>
|
||||
/// <param name="variable">The CVar object to add</param>
|
||||
public void SetNew(CVar variable)
|
||||
{
|
||||
if (!VariableExists(variable.Name)) Set(variable);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a non-hidden variable given its name.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the variable to remove</param>
|
||||
public void Remove(string name)
|
||||
{
|
||||
All.RemoveAll(v => v.Name == name && !v.Hidden);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>Removes all non-hidden variables if their name matches a condition.</para>
|
||||
/// <para>The name of the variables in the list are the left-hand term of the comparison.</para>
|
||||
/// </summary>
|
||||
/// <param name="cond">The condition type</param>
|
||||
/// <param name="name">The right-hand term of the comparison</param>
|
||||
/// <param name="data">The BotData object used for variable replacement</param>
|
||||
public void Remove(Condition cond, string name, BotData data)
|
||||
{
|
||||
All.RemoveAll(v => ConditionChecker.Verify(v.Name, cond, name, data) && !v.Hidden);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a string where all the variables marked as Capture are printed in an organized fashion.
|
||||
/// </summary>
|
||||
/// <returns>The chained capture string.</returns>
|
||||
public string ToCaptureString()
|
||||
{
|
||||
return string.Join(" | ", Captures.Select(c => c.Name + " = " + c.ToString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace RuriLib.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a file as a source of input data that needs to be tested against a Config by the Runner.
|
||||
/// </summary>
|
||||
public class Wordlist
|
||||
{
|
||||
/// <summary>Needed for NoSQL storage.</summary>
|
||||
public Guid Id { get; set; }
|
||||
|
||||
/// <summary>The name of the Wordlist.</summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>The path where the file is stored on disk.</summary>
|
||||
public string Path { get; set; }
|
||||
|
||||
/// <summary>The WordlistType as a string (since the WordlistTypes in the Environment file are editable).</summary>
|
||||
public string Type { get; set; }
|
||||
|
||||
/// <summary>The purpose for which the Wordlist should be used.</summary>
|
||||
public string Purpose { get; set; }
|
||||
|
||||
/// <summary>The total number of data lines of the file.</summary>
|
||||
public int Total { get; set; }
|
||||
|
||||
/// <summary>Needed for NoSQL deserialization.</summary>
|
||||
public Wordlist() { }
|
||||
|
||||
/// <summary>
|
||||
/// Creates an instance of a Wordlist.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the Wordlist</param>
|
||||
/// <param name="path">The path to the file on disk</param>
|
||||
/// <param name="type">The WordlistType as a string</param>
|
||||
/// <param name="purpose">The purpose of the Wordlist</param>
|
||||
/// <param name="countLines">Whether to enumerate the total number of data lines in the Wordlist</param>
|
||||
public Wordlist(string name, string path, string type, string purpose, bool countLines = true)
|
||||
{
|
||||
Name = name;
|
||||
Path = path;
|
||||
Type = type;
|
||||
Purpose = purpose;
|
||||
Total = 0;
|
||||
if (countLines)
|
||||
{
|
||||
try
|
||||
{
|
||||
Total = File.ReadLines(path).Count();
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace RuriLib.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Class that represents a type of Wordlist with rules to check the validity and to slice the data given a separator.
|
||||
/// </summary>
|
||||
public class WordlistType
|
||||
{
|
||||
/// <summary>Whether to check if the regex successfully matches the input data.</summary>
|
||||
public bool Verify { get; set; } = false;
|
||||
|
||||
/// <summary>The regular expression that validates the input data.</summary>
|
||||
public string Regex { get; set; } = "^.*$";
|
||||
|
||||
/// <summary>The name of the Wordlist Type.</summary>
|
||||
public string Name { get; set; } = "Default";
|
||||
|
||||
/// <summary>The separator used for slicing the input data into a list of strings.</summary>
|
||||
public string Separator { get; set; } = ":";
|
||||
|
||||
/// <summary>
|
||||
/// <para>The list of variable names of the slices that will be created from the input data.</para>
|
||||
/// <para>Each slice should be used to generate a pair of values with a variable name and a value taken from the split input data.</para>
|
||||
/// </summary>
|
||||
public List<string> Slices { get; set; } = new List<string>();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// Le informazioni generali relative a un assembly sono controllate dal seguente
|
||||
// set di attributi. Modificare i valori di questi attributi per modificare le informazioni
|
||||
// associate a un assembly.
|
||||
[assembly: AssemblyTitle("RuriLib")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("RuriLib")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2018")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Se si imposta ComVisible su false, i tipi in questo assembly non saranno visibili
|
||||
// ai componenti COM. Se è necessario accedere a un tipo in questo assembly da
|
||||
// COM, impostare su true l'attributo ComVisible per tale tipo.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// Se il progetto viene esposto a COM, il GUID seguente verrà utilizzato come ID della libreria dei tipi
|
||||
[assembly: Guid("17e95d0b-c1fd-445c-9fdf-244af7299a67")]
|
||||
|
||||
// Le informazioni sulla versione di un assembly sono costituite dai seguenti quattro valori:
|
||||
//
|
||||
// Versione principale
|
||||
// Versione secondaria
|
||||
// Numero di build
|
||||
// Revisione
|
||||
//
|
||||
// È possibile specificare tutti i valori oppure impostare valori predefiniti per i numeri relativi alla revisione e alla build
|
||||
// usando l'asterisco '*' come illustrato di seguito:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
@@ -0,0 +1,62 @@
|
||||
using System.ComponentModel;
|
||||
using System.Threading;
|
||||
|
||||
namespace RuriLib.Runner
|
||||
{
|
||||
/// <summary>
|
||||
/// Whether the AbortableBackgroundWorker is idle, stopping or working.
|
||||
/// </summary>
|
||||
public enum WorkerStatus
|
||||
{
|
||||
/// <summary>The Worker is not working.</summary>
|
||||
Idle,
|
||||
|
||||
/// <summary>The Worker is working.</summary>
|
||||
Running,
|
||||
|
||||
/// <summary>The Worker is cancelling its work.</summary>
|
||||
Stopping
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A Worker that can be aborted like normal threads.
|
||||
/// </summary>
|
||||
public class AbortableBackgroundWorker : BackgroundWorker
|
||||
{
|
||||
private Thread workerThread;
|
||||
|
||||
/// <summary>The Status of the Worker.</summary>
|
||||
public WorkerStatus Status { get; set; }
|
||||
|
||||
/// <summary>The Id of the Worker.</summary>
|
||||
public int Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// OnDoWork that supports aborting.
|
||||
/// </summary>
|
||||
/// <param name="e">The DoWorkEventArgs</param>
|
||||
protected override void OnDoWork(DoWorkEventArgs e)
|
||||
{
|
||||
workerThread = Thread.CurrentThread;
|
||||
try
|
||||
{
|
||||
base.OnDoWork(e);
|
||||
}
|
||||
catch (ThreadAbortException)
|
||||
{
|
||||
e.Cancel = true; //We must set Cancel property to true!
|
||||
Thread.ResetAbort(); //Prevents ThreadAbortException propagation
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Calls the Abort() method on the inner Thread.</summary>
|
||||
public void Abort()
|
||||
{
|
||||
if (workerThread != null)
|
||||
{
|
||||
workerThread.Abort();
|
||||
workerThread = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
using RuriLib.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace RuriLib.Runner
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface used to communicate between the RunnerViewModel and the corresponding View.
|
||||
/// </summary>
|
||||
public interface IRunnerMessaging
|
||||
{
|
||||
/// <summary>A message has arrived.</summary>
|
||||
event Action<IRunnerMessaging, LogLevel, string, bool> MessageArrived;
|
||||
|
||||
/// <summary>The status of the Master Worker changed.</summary>
|
||||
event Action<IRunnerMessaging> WorkerStatusChanged;
|
||||
|
||||
/// <summary>A Hit was found.</summary>
|
||||
event Action<IRunnerMessaging, Hit> FoundHit;
|
||||
|
||||
/// <summary>The proxies need to be reloaded.</summary>
|
||||
event Action<IRunnerMessaging> ReloadProxies;
|
||||
|
||||
/// <summary>An Action needs to be executed on the main thread.</summary>
|
||||
event Action<IRunnerMessaging, Action> DispatchAction;
|
||||
|
||||
/// <summary>The progress needs to be saved.</summary>
|
||||
event Action<IRunnerMessaging> SaveProgress;
|
||||
|
||||
/// <summary>The user needs to type the custom inputs.</summary>
|
||||
event Action<IRunnerMessaging> AskCustomInputs;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
using OpenQA.Selenium.Remote;
|
||||
using RuriLib.ViewModels;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace RuriLib.Runner
|
||||
{
|
||||
/// <summary>
|
||||
/// Bot class. Includes a Worker that is used to perform checks on input data in the Runner.
|
||||
/// </summary>
|
||||
public class RunnerBotViewModel : ViewModelBase
|
||||
{
|
||||
private int id;
|
||||
/// <summary>The unique id of the bot (usually between 1 and the total amount of Bots).</summary>
|
||||
public int Id { get { return id; } set { id = value; OnPropertyChanged(); } }
|
||||
|
||||
private string data = "";
|
||||
/// <summary>The data that needs to be checked.</summary>
|
||||
public string Data { get { return data; } set { data = value; OnPropertyChanged(); } }
|
||||
|
||||
private string proxy = "";
|
||||
/// <summary>The proxy that the Worker is using to perform requests.</summary>
|
||||
public string Proxy { get { return proxy; } set { proxy = value; OnPropertyChanged(); } }
|
||||
|
||||
private string status = "Initializing...";
|
||||
/// <summary>The status of the bot.</summary>
|
||||
public string Status { get { return status; } set { status = value; OnPropertyChanged(); } }
|
||||
|
||||
/// <summary>The Worker that performs the checks.</summary>
|
||||
public AbortableBackgroundWorker Worker { get; set; }
|
||||
|
||||
/// <summary>The Selenium WebDriver.</summary>
|
||||
public RemoteWebDriver Driver { get; set; } = null;
|
||||
|
||||
/// <summary>Whether the Selenium WebDriver is open or not.</summary>
|
||||
public bool IsDriverOpen { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Creates an instance of a bot given an id.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique id that will be assigned to the bot.</param>
|
||||
public RunnerBotViewModel(int id)
|
||||
{
|
||||
Id = id;
|
||||
Worker = new AbortableBackgroundWorker();
|
||||
Worker.WorkerSupportsCancellation = true;
|
||||
Worker.Id = id;
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,216 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{17E95D0B-C1FD-445C-9FDF-244AF7299A67}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>RuriLib</RootNamespace>
|
||||
<AssemblyName>RuriLib</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<NuGetPackageImportStamp>
|
||||
</NuGetPackageImportStamp>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<DocumentationFile>bin\Debug\RuriLib.xml</DocumentationFile>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="AngleSharp, Version=0.9.9.0, Culture=neutral, PublicKeyToken=e83494dcdc6d31ea, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\AngleSharp.0.9.9.2\lib\net45\AngleSharp.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Extreme.Net, Version=1.1.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\..\Extreme-original\Extreme.Net\bin\Debug\Extreme.Net.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="IronPython, Version=2.7.9.0, Culture=neutral, PublicKeyToken=7f709c5b713576e1, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\IronPython.2.7.9\lib\net45\IronPython.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="IronPython.Modules, Version=2.7.9.0, Culture=neutral, PublicKeyToken=7f709c5b713576e1, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\IronPython.2.7.9\lib\net45\IronPython.Modules.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="IronPython.SQLite, Version=2.7.9.0, Culture=neutral, PublicKeyToken=7f709c5b713576e1, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\IronPython.2.7.9\lib\net45\IronPython.SQLite.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="IronPython.Wpf, Version=2.7.9.0, Culture=neutral, PublicKeyToken=7f709c5b713576e1, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\IronPython.2.7.9\lib\net45\IronPython.Wpf.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Jint, Version=0.0.0.0, Culture=neutral, PublicKeyToken=2e92ba9c8d81157f, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Jint.2.11.58\lib\net451\Jint.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Leaf.xNet, Version=5.1.74.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Leaf.xNet.5.1.74\lib\net452\Leaf.xNet.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="LiteDB, Version=4.1.4.0, Culture=neutral, PublicKeyToken=4ee40123013c9f27, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\LiteDB.4.1.4\lib\net40\LiteDB.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Dynamic, Version=1.2.2.0, Culture=neutral, PublicKeyToken=7f709c5b713576e1, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\DynamicLanguageRuntime.1.2.2\lib\net45\Microsoft.Dynamic.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Owin, Version=4.0.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Owin.4.0.1\lib\net45\Microsoft.Owin.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Owin.Security, Version=4.0.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Owin.Security.4.0.1\lib\net45\Microsoft.Owin.Security.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Scripting, Version=1.2.2.0, Culture=neutral, PublicKeyToken=7f709c5b713576e1, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\DynamicLanguageRuntime.1.2.2\lib\net45\Microsoft.Scripting.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Scripting.Metadata, Version=1.2.2.0, Culture=neutral, PublicKeyToken=7f709c5b713576e1, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\DynamicLanguageRuntime.1.2.2\lib\net45\Microsoft.Scripting.Metadata.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Newtonsoft.Json, Version=12.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Newtonsoft.Json.12.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Owin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f0ebd12fd5e55cc5, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Owin.1.0\lib\net40\Owin.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="PresentationCore" />
|
||||
<Reference Include="ProxySocket, Version=1.1.2.0, Culture=neutral, PublicKeyToken=966874d7118d1436, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\ProxySocket.1.1.2\lib\net40\ProxySocket.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Drawing" />
|
||||
<Reference Include="System.Management" />
|
||||
<Reference Include="System.Net.Http, Version=4.1.1.3, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Net.Http.4.3.4\lib\net46\System.Net.Http.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Numerics" />
|
||||
<Reference Include="System.Runtime.CompilerServices.Unsafe, Version=4.0.4.1, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Runtime.CompilerServices.Unsafe.4.5.2\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Security.Cryptography.Algorithms, Version=4.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Security.Cryptography.Algorithms.4.3.1\lib\net461\System.Security.Cryptography.Algorithms.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Security.Cryptography.Encoding, Version=4.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Security.Cryptography.Encoding.4.3.0\lib\net46\System.Security.Cryptography.Encoding.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Security.Cryptography.Primitives, Version=4.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Security.Cryptography.Primitives.4.3.0\lib\net46\System.Security.Cryptography.Primitives.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Security.Cryptography.X509Certificates, Version=4.1.1.2, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Security.Cryptography.X509Certificates.4.3.2\lib\net461\System.Security.Cryptography.X509Certificates.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Text.Encoding.CodePages, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Text.Encoding.CodePages.4.5.1\lib\net461\System.Text.Encoding.CodePages.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="WebDriver, Version=3.141.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Selenium.WebDriver.3.141.0\lib\net45\WebDriver.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="WebDriver.Support, Version=3.141.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Selenium.Support.3.141.0\lib\net45\WebDriver.Support.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Blocks\BlockLSCode.cs" />
|
||||
<Compile Include="Blocks\BlockTCP.cs" />
|
||||
<Compile Include="CaptchaServices\CaptchasIO.cs" />
|
||||
<Compile Include="LoliScript\Exceptions\BlockProcessingException.cs" />
|
||||
<Compile Include="LoliScript\LoliScript.cs" />
|
||||
<Compile Include="LoliScript\Parser\BlockParser.cs" />
|
||||
<Compile Include="LoliScript\Parser\CommandParser.cs" />
|
||||
<Compile Include="LoliScript\Parser\MouseActionParser.cs" />
|
||||
<Compile Include="LoliScript\Writer\BlockWriter.cs" />
|
||||
<Compile Include="Models\DataPool.cs" />
|
||||
<Compile Include="Models\ProxyPool.cs" />
|
||||
<Compile Include="Runner\AbortableBackgroundWorker.cs">
|
||||
<SubType>Component</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Blocks\BlockBase.cs" />
|
||||
<Compile Include="Blocks\BlockBypassCF.cs" />
|
||||
<Compile Include="Blocks\BlockImageCaptcha.cs" />
|
||||
<Compile Include="Blocks\BlockCaptcha.cs" />
|
||||
<Compile Include="Blocks\BlockFunction.cs" />
|
||||
<Compile Include="Blocks\BlockKeycheck.cs" />
|
||||
<Compile Include="Blocks\BlockParse.cs" />
|
||||
<Compile Include="Blocks\BlockRecaptcha.cs" />
|
||||
<Compile Include="Blocks\BlockRequest.cs" />
|
||||
<Compile Include="Blocks\BlockUtility.cs" />
|
||||
<Compile Include="CaptchaServices\AntiCaptcha.cs" />
|
||||
<Compile Include="CaptchaServices\AZCaptcha.cs" />
|
||||
<Compile Include="CaptchaServices\CaptchaService.cs" />
|
||||
<Compile Include="CaptchaServices\CWebClient.cs">
|
||||
<SubType>Component</SubType>
|
||||
</Compile>
|
||||
<Compile Include="CaptchaServices\DeathByCaptcha.cs" />
|
||||
<Compile Include="CaptchaServices\DeCaptcher.cs" />
|
||||
<Compile Include="CaptchaServices\ImageTyperz.cs" />
|
||||
<Compile Include="CaptchaServices\SolveReCaptcha.cs" />
|
||||
<Compile Include="CaptchaServices\TwoCaptcha.cs" />
|
||||
<Compile Include="ConditionChecker.cs" />
|
||||
<Compile Include="IOManager.cs" />
|
||||
<Compile Include="LoliScript\Parser\LineParser.cs" />
|
||||
<Compile Include="LoliScript\Parser\DeleteParser.cs" />
|
||||
<Compile Include="LoliScript\Parser\SetParser.cs" />
|
||||
<Compile Include="Models\CData.cs" />
|
||||
<Compile Include="Models\Config.cs" />
|
||||
<Compile Include="Models\ConfigSettings.cs" />
|
||||
<Compile Include="Models\ExportFormat.cs" />
|
||||
<Compile Include="Models\Record.cs" />
|
||||
<Compile Include="Models\CVar.cs" />
|
||||
<Compile Include="Models\Wordlist.cs" />
|
||||
<Compile Include="Runner\IRunnerMessaging.cs" />
|
||||
<Compile Include="Runner\RunnerBotViewModel.cs" />
|
||||
<Compile Include="Runner\RunnerViewModel.cs" />
|
||||
<Compile Include="TypeSwitch.cs" />
|
||||
<Compile Include="ViewModels\ConfigViewModel.cs" />
|
||||
<Compile Include="Models\CProxy.cs" />
|
||||
<Compile Include="Models\LogEntry.cs" />
|
||||
<Compile Include="Models\Hit.cs" />
|
||||
<Compile Include="Models\CustomInput.cs" />
|
||||
<Compile Include="Models\CustomKeychain.cs" />
|
||||
<Compile Include="Models\EnvironmentSettings.cs" />
|
||||
<Compile Include="Models\DataRule.cs" />
|
||||
<Compile Include="ViewModels\RLSettings\SettingsCaptchas.cs" />
|
||||
<Compile Include="ViewModels\RLSettings\SettingsGeneral.cs" />
|
||||
<Compile Include="ViewModels\RLSettings\SettingsProxies.cs" />
|
||||
<Compile Include="ViewModels\RLSettings\SettingsSelenium.cs" />
|
||||
<Compile Include="ViewModels\RLSettingsViewModel.cs" />
|
||||
<Compile Include="ViewModels\ViewModelBase.cs" />
|
||||
<Compile Include="Models\WordlistType.cs" />
|
||||
<None Include="app.config" />
|
||||
<Compile Include="Models\BotData.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Models\KeyChain.cs" />
|
||||
<Compile Include="Models\Key.cs" />
|
||||
<Compile Include="Blocks\SBlockBrowserAction.cs" />
|
||||
<Compile Include="Blocks\SBlockElementAction.cs" />
|
||||
<Compile Include="Blocks\SBlockExecuteJS.cs" />
|
||||
<Compile Include="Blocks\SBlockNavigate.cs" />
|
||||
<Compile Include="Models\ValidData.cs" />
|
||||
<Compile Include="Models\VariableList.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<PropertyGroup>
|
||||
<PostBuildEvent>
|
||||
</PostBuildEvent>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,27 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace RuriLib
|
||||
{
|
||||
/// <summary>
|
||||
/// Allows to make a switch statement on the Type of the object, and executes a different Action depending on its outcome.
|
||||
/// </summary>
|
||||
public class TypeSwitch
|
||||
{
|
||||
Dictionary<Type, Action<object>> matches = new Dictionary<Type, Action<object>>();
|
||||
|
||||
/// <summary>
|
||||
/// Adds a case to the TypeSwitch.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type</typeparam>
|
||||
/// <param name="action">The action to perform</param>
|
||||
/// <returns>The TypeSwitch itself</returns>
|
||||
public TypeSwitch Case<T>(Action<T> action) { matches.Add(typeof(T), (x) => action((T)x)); return this; }
|
||||
|
||||
/// <summary>
|
||||
/// Runs the switch statement on an object and executes the corresponding Action.
|
||||
/// </summary>
|
||||
/// <param name="x">The object on which you want to execute the switch statement</param>
|
||||
public void Switch(object x) { matches[x.GetType()](x); }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace RuriLib.ViewModels
|
||||
{
|
||||
/// <summary>
|
||||
/// An observable wrapper around a Config object.
|
||||
/// </summary>
|
||||
public class ConfigViewModel : ViewModelBase
|
||||
{
|
||||
/// <summary>The actual Config object.</summary>
|
||||
public Config Config { get; set; }
|
||||
|
||||
private string category = "Default";
|
||||
/// <summary>The Category of the config.</summary>
|
||||
public string Category { get { return category; } set { category = value; OnPropertyChanged(); } }
|
||||
|
||||
private string path = "";
|
||||
/// <summary>The path of the config file on disk.</summary>
|
||||
public string Path { get { return path; } set { path = value; OnPropertyChanged(); } }
|
||||
|
||||
/// <summary>The name of the config.</summary>
|
||||
public string Name { get { return Config.Settings.Name; } }
|
||||
|
||||
/// <summary>
|
||||
/// Constructs an instance of the ConfigViewModel class.
|
||||
/// </summary>
|
||||
/// <param name="path">The path of the config file on disk</param>
|
||||
/// <param name="category">The category of the config</param>
|
||||
/// <param name="config">The actual Config object</param>
|
||||
public ConfigViewModel(string path, string category, Config config)
|
||||
{
|
||||
Path = path;
|
||||
Category = category;
|
||||
Config = config;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
namespace RuriLib.ViewModels
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides captcha-related settings.
|
||||
/// </summary>
|
||||
public class SettingsCaptchas : ViewModelBase
|
||||
{
|
||||
private BlockCaptcha.CaptchaService currentService = BlockCaptcha.CaptchaService.AntiCaptcha;
|
||||
/// <summary>Which Captcha Service is currently selected for solving captcha challenges.</summary>
|
||||
public BlockCaptcha.CaptchaService CurrentService { get { return currentService; } set { currentService = value; OnPropertyChanged(); } }
|
||||
|
||||
private string antiCapToken = "";
|
||||
/// <summary>The AntiCaptcha API Token.</summary>
|
||||
public string AntiCapToken { get { return antiCapToken; } set { antiCapToken = value; OnPropertyChanged(); } }
|
||||
|
||||
private string imageTypToken = "";
|
||||
/// <summary>The ImageTyperz API Token.</summary>
|
||||
public string ImageTypToken { get { return imageTypToken; } set { imageTypToken = value; OnPropertyChanged(); } }
|
||||
|
||||
private string dbcUser = "";
|
||||
/// <summary>The DeathByCaptcha Username.</summary>
|
||||
public string DBCUser { get { return dbcUser; } set { dbcUser = value; OnPropertyChanged(); } }
|
||||
|
||||
private string dbcPass = "";
|
||||
/// <summary>The DeathByCaptcha Password.</summary>
|
||||
public string DBCPass { get { return dbcPass; } set { dbcPass = value; OnPropertyChanged(); } }
|
||||
|
||||
private string twoCapToken = "";
|
||||
/// <summary>The TwoCaptcha API Token.</summary>
|
||||
public string TwoCapToken { get { return twoCapToken; } set { twoCapToken = value; OnPropertyChanged(); } }
|
||||
|
||||
private string dcUser = "";
|
||||
/// <summary>The DeCaptcher Username.</summary>
|
||||
public string DCUser { get { return dcUser; } set { dcUser = value; OnPropertyChanged(); } }
|
||||
|
||||
private string dcPass = "";
|
||||
/// <summary>The DeCaptcher Password.</summary>
|
||||
public string DCPass { get { return dcPass; } set { dcPass = value; OnPropertyChanged(); } }
|
||||
|
||||
private string azCapToken = "";
|
||||
/// <summary>The AZCaptcha API Token.</summary>
|
||||
public string AZCapToken { get { return azCapToken; } set { azCapToken = value; OnPropertyChanged(); } }
|
||||
|
||||
private string srUserId = "";
|
||||
/// <summary>The SolveReCaptcha User Id.</summary>
|
||||
public string SRUserId { get { return srUserId; } set { srUserId = value; OnPropertyChanged(); } }
|
||||
|
||||
private string srToken = "";
|
||||
/// <summary>The SolveReCaptcha API Token.</summary>
|
||||
public string SRToken { get { return srToken; } set { srToken = value; OnPropertyChanged(); } }
|
||||
|
||||
private string cIOToken = "";
|
||||
/// <summary>The CaptchasIO API Token.</summary>
|
||||
public string CIOToken { get { return cIOToken; } set { cIOToken = value; OnPropertyChanged(); } }
|
||||
|
||||
private string cdToken = "";
|
||||
/// <summary>The CaptchaDecoder API Token.</summary>
|
||||
public string CDToken { get { return cdToken; } set { cdToken = value; OnPropertyChanged(); } }
|
||||
|
||||
private bool bypassBalanceCheck = false;
|
||||
/// <summary>Whether to bypass the balance check before solving a captcha challenge.</summary>
|
||||
public bool BypassBalanceCheck { get { return bypassBalanceCheck; } set { bypassBalanceCheck = value; OnPropertyChanged(); } }
|
||||
|
||||
private int timeout = 120;
|
||||
/// <summary>The maximum amount of time to wait until a captcha challenge is solved.</summary>
|
||||
public int Timeout { get { return timeout; } set { timeout = value; OnPropertyChanged(); } }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
namespace RuriLib.ViewModels
|
||||
{
|
||||
/// <summary>
|
||||
/// The level of detail of a bot's status.
|
||||
/// </summary>
|
||||
public enum BotsDisplayMode
|
||||
{
|
||||
/// <summary>No information is displayed.</summary>
|
||||
None,
|
||||
|
||||
/// <summary>Every information is displayed.</summary>
|
||||
Everything,
|
||||
|
||||
/// <summary>Only information about the end result is displayed.</summary>
|
||||
EndResultOnly
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides settings for general purposes.
|
||||
/// </summary>
|
||||
public class SettingsGeneral : ViewModelBase
|
||||
{
|
||||
private int _waitTime = 100;
|
||||
/// <summary>The amount of time the Bot needs to wait after completing a check, in milliseconds.</summary>
|
||||
public int WaitTime { get { return _waitTime; } set { _waitTime = value; OnPropertyChanged(); } }
|
||||
|
||||
private int _requestsTimeout = 10;
|
||||
/// <summary>The maximum amount of time to wait for a response from the server for any HTTP request, in seconds.</summary>
|
||||
public int RequestTimeout { get { return _requestsTimeout; } set { _requestsTimeout = value; OnPropertyChanged(); } }
|
||||
|
||||
private int _maxHits = 0;
|
||||
/// <summary>The maximum number of hits before the Runner automatically stops (0 for infinite).</summary>
|
||||
public int MaxHits { get { return _maxHits; } set { _maxHits = value; OnPropertyChanged(); } }
|
||||
|
||||
private BotsDisplayMode _botsDisplayMode = BotsDisplayMode.Everything;
|
||||
/// <summary>The rate and detail of information to display in the Bot Status column.</summary>
|
||||
public BotsDisplayMode BotsDisplayMode { get { return _botsDisplayMode; } set { _botsDisplayMode = value; OnPropertyChanged(); } }
|
||||
|
||||
private bool _enableBotLog = false;
|
||||
/// <summary>Whether to keep the LogBuffer from BotData stored for future use (slows down the Runner).</summary>
|
||||
public bool EnableBotLog { get { return _enableBotLog; } set { _enableBotLog = value; OnPropertyChanged(); } }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
using Extreme.Net;
|
||||
|
||||
namespace RuriLib.ViewModels
|
||||
{
|
||||
/// <summary>
|
||||
/// The Source of proxies that gets queried when all proxies are banned in order to get fresh ones.
|
||||
/// </summary>
|
||||
public enum ProxyReloadSource
|
||||
{
|
||||
/// <summary>The ProxyManager.</summary>
|
||||
Manager,
|
||||
/// <summary>A file on the disk.</summary>
|
||||
File,
|
||||
/// <summary>An online API.</summary>
|
||||
API
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides proxy-related settings.
|
||||
/// </summary>
|
||||
public class SettingsProxies : ViewModelBase
|
||||
{
|
||||
#region General
|
||||
private bool concurrentUse = false;
|
||||
/// <summary>Whether to allow two Bots to use the same proxy.</summary>
|
||||
public bool ConcurrentUse { get { return concurrentUse; } set { concurrentUse = value; OnPropertyChanged(); } }
|
||||
|
||||
private bool neverBan = false;
|
||||
/// <summary>Whether to never ban the proxies.</summary>
|
||||
public bool NeverBan { get { return neverBan; } set { neverBan = value; OnPropertyChanged(); } }
|
||||
|
||||
private bool banAfterGoodStatus = false;
|
||||
/// <summary>Whether to never ban the proxy after a SUCCESS, CUSTOM or NONE status (not FAIL or RETRY).</summary>
|
||||
public bool BanAfterGoodStatus { get { return banAfterGoodStatus; } set { banAfterGoodStatus = value; OnPropertyChanged(); } }
|
||||
#endregion
|
||||
|
||||
#region Reload
|
||||
private bool reload = true;
|
||||
/// <summary>Whether to reload the proxies from the proxy source after they are all banned.</summary>
|
||||
public bool Reload { get { return reload; } set { reload = value; OnPropertyChanged(); } }
|
||||
|
||||
private ProxyReloadSource reloadSource = ProxyReloadSource.Manager;
|
||||
/// <summary>The source to reload the proxies from.</summary>
|
||||
public ProxyReloadSource ReloadSource { get { return reloadSource; } set { reloadSource = value; OnPropertyChanged(); } }
|
||||
|
||||
private string reloadPath = "";
|
||||
/// <summary>The API URL or the file path on disk.</summary>
|
||||
public string ReloadPath { get { return reloadPath; } set { reloadPath = value; OnPropertyChanged(); } }
|
||||
|
||||
private ProxyType reloadType = ProxyType.Http;
|
||||
/// <summary>The Type of the proxies to load.</summary>
|
||||
public ProxyType ReloadType { get { return reloadType; } set { reloadType = value; OnPropertyChanged(); } }
|
||||
|
||||
private bool parseWithIPRegex = false;
|
||||
/// <summary>
|
||||
/// <para>Whether to use an IP:PORT regex to extract the proxies from the source.</para>
|
||||
/// <para>The regex will match proxies of the form 127.0.0.1:8888.</para>
|
||||
/// <para>This is useful for example when the server returns proxies in a HTML, XML or JSON format and not one proxy per line.</para>
|
||||
/// <para>This is also useful when the server returns proxies with unix-like line endings.</para>
|
||||
/// </summary>
|
||||
public bool ParseWithIPRegex { get { return parseWithIPRegex; } set { parseWithIPRegex = value; OnPropertyChanged(); } }
|
||||
#endregion
|
||||
|
||||
#region Cloudflare
|
||||
private bool alwaysGetClearance = false;
|
||||
/// <summary>Whether to avoid storing the clearance cookie for future use when querying the site from the same IP (proxy).</summary>
|
||||
public bool AlwaysGetClearance { get { return alwaysGetClearance; } set { alwaysGetClearance = value; OnPropertyChanged(); } }
|
||||
#endregion
|
||||
|
||||
#region Global Keys
|
||||
private string[] globalBanKeys = new string[] { };
|
||||
/// <summary>
|
||||
/// <para>The array of possible bad replies from a proxy server.</para>
|
||||
/// <para>The proxy will be banned when one of the keys is found.</para>
|
||||
/// <para>These are useful to immediately blacklist proxies that need a captive portal or that only accept IPs from a given region.</para>
|
||||
/// </summary>
|
||||
public string[] GlobalBanKeys { get { return globalBanKeys; } set { globalBanKeys = value; OnPropertyChanged(); } }
|
||||
|
||||
private string[] globalRetryKeys = new string[] { };
|
||||
/// <summary>
|
||||
/// <para>The array of possible temporary bad replies from a proxy server.</para>
|
||||
/// <para>The proxy will be retried on the next check.</para>
|
||||
/// <para>These are useful when the proxy is temporarily too busy because of high traffic.</para>
|
||||
/// </summary>
|
||||
public string[] GlobalRetryKeys { get { return globalRetryKeys; } set { globalRetryKeys = value; OnPropertyChanged(); } }
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace RuriLib.ViewModels
|
||||
{
|
||||
/// <summary>
|
||||
/// The Type of supported Browser to drive via selenium.
|
||||
/// </summary>
|
||||
public enum BrowserType
|
||||
{
|
||||
/// <summary>Google Chrome</summary>
|
||||
Chrome,
|
||||
/// <summary>Mozilla Firefox</summary>
|
||||
Firefox
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides selenium-related settings.
|
||||
/// </summary>
|
||||
public class SettingsSelenium : ViewModelBase
|
||||
{
|
||||
private BrowserType browser = BrowserType.Chrome;
|
||||
/// <summary>The Browser to be used.</summary>
|
||||
public BrowserType Browser { get { return browser; } set { browser = value; OnPropertyChanged(); } }
|
||||
|
||||
private bool headless = false;
|
||||
/// <summary>Whether to run the browser in Headless mode (--headless argument).</summary>
|
||||
public bool Headless { get { return headless; } set { headless = value; OnPropertyChanged(); } }
|
||||
|
||||
private string firefoxBinaryLocation = @"C:\Program Files (x86)\Mozilla Firefox\firefox.exe";
|
||||
/// <summary>The location of the firefox.exe binary on disk.</summary>
|
||||
public string FirefoxBinaryLocation { get { return firefoxBinaryLocation; } set { firefoxBinaryLocation = value; OnPropertyChanged(); } }
|
||||
|
||||
private string chromeBinaryLocation = @"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe";
|
||||
/// <summary>The location of the chrome.exe binary on disk.</summary>
|
||||
public string ChromeBinaryLocation { get { return chromeBinaryLocation; } set { chromeBinaryLocation = value; OnPropertyChanged(); } }
|
||||
|
||||
private List<string> chromeExtensions = new List<string>();
|
||||
/// <summary>The list of .crx extensions for Chrome inside the ChromeExtensions folder. Extensions don't work in Headless mode!</summary>
|
||||
public List<string> ChromeExtensions { get { return chromeExtensions; } set { chromeExtensions = value; OnPropertyChanged(); } }
|
||||
|
||||
private bool drawMouseMovement = true;
|
||||
/// <summary>Whether to draw red dots to follow the mouse movement when a MOUSEACTION command is issued.</summary>
|
||||
public bool DrawMouseMovement { get { return drawMouseMovement; } set { drawMouseMovement = value; OnPropertyChanged(); } }
|
||||
|
||||
private int pageLoadTimeout = 60;
|
||||
/// <summary>The default timeout for page load in the browser.</summary>
|
||||
public int PageLoadTimeout { get { return pageLoadTimeout; } set { pageLoadTimeout = value; OnPropertyChanged(); } }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
namespace RuriLib.ViewModels
|
||||
{
|
||||
/// <summary>
|
||||
/// The settings used across RuriLib classes. Contains the smaller settings categories.
|
||||
/// </summary>
|
||||
public class RLSettingsViewModel
|
||||
{
|
||||
/// <summary>The General Settings of RuriLib.</summary>
|
||||
public SettingsGeneral General { get; set; } = new SettingsGeneral();
|
||||
/// <summary>The Proxy Settings of RuriLib.</summary>
|
||||
public SettingsProxies Proxies { get; set; } = new SettingsProxies();
|
||||
/// <summary>The Captcha Settings of RuriLib.</summary>
|
||||
public SettingsCaptchas Captchas { get; set; } = new SettingsCaptchas();
|
||||
/// <summary>The Selenium Settings of RuriLib.</summary>
|
||||
public SettingsSelenium Selenium { get; set; } = new SettingsSelenium();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace RuriLib.ViewModels
|
||||
{
|
||||
/// <summary>
|
||||
/// A basic viewmodel that implements the PropertyChanged event.
|
||||
/// </summary>
|
||||
public abstract class ViewModelBase : INotifyPropertyChanged
|
||||
{
|
||||
/// <summary>The event that lets the GUI know a property was changed.</summary>
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Raises a PropertyChanged event.
|
||||
/// </summary>
|
||||
/// <param name="propertyName">The name of the property. If null, the name of the calling property will be used.</param>
|
||||
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<runtime>
|
||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<probing privatePath="bin" />
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Text.Encoding.CodePages" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.1.1.0" newVersion="4.1.1.0" />
|
||||
</dependentAssembly>
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
</configuration>
|
||||
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="AngleSharp" version="0.9.9.2" targetFramework="net461" />
|
||||
<package id="DynamicLanguageRuntime" version="1.2.2" targetFramework="net461" />
|
||||
<package id="IronPython" version="2.7.9" targetFramework="net461" />
|
||||
<package id="Jint" version="2.11.58" targetFramework="net461" />
|
||||
<package id="Leaf.xNet" version="5.1.74" targetFramework="net461" />
|
||||
<package id="LiteDB" version="4.1.4" targetFramework="net461" />
|
||||
<package id="Microsoft.Owin" version="4.0.1" targetFramework="net461" />
|
||||
<package id="Microsoft.Owin.Security" version="4.0.1" targetFramework="net461" />
|
||||
<package id="Newtonsoft.Json" version="12.0.1" targetFramework="net461" />
|
||||
<package id="Owin" version="1.0" targetFramework="net461" />
|
||||
<package id="ProxySocket" version="1.1.2" targetFramework="net461" />
|
||||
<package id="Selenium.Support" version="3.141.0" targetFramework="net461" />
|
||||
<package id="Selenium.WebDriver" version="3.141.0" targetFramework="net461" />
|
||||
<package id="System.Net.Http" version="4.3.4" targetFramework="net461" />
|
||||
<package id="System.Runtime.CompilerServices.Unsafe" version="4.5.2" targetFramework="net461" />
|
||||
<package id="System.Security.Cryptography.Algorithms" version="4.3.1" targetFramework="net461" />
|
||||
<package id="System.Security.Cryptography.Encoding" version="4.3.0" targetFramework="net461" />
|
||||
<package id="System.Security.Cryptography.Primitives" version="4.3.0" targetFramework="net461" />
|
||||
<package id="System.Security.Cryptography.X509Certificates" version="4.3.2" targetFramework="net461" />
|
||||
<package id="System.Text.Encoding.CodePages" version="4.5.1" targetFramework="net461" />
|
||||
</packages>
|
||||
Reference in New Issue
Block a user