using Extreme.Net;
using LiteDB;
using System;
using System.Linq;
using System.Text.RegularExpressions;
namespace RuriLib.Models
{
///
/// The Status of a CProxy.
///
public enum Status
{
/// The proxy is not assigned to any bot.
AVAILABLE,
/// The proxy is assigned to at least 1 bot.
BUSY,
/// The proxy didn't accept the connection.
BAD,
/// The proxy was banned.
BANNED
}
///
/// The Working Status of a CProxy.
///
public enum ProxyWorking
{
/// The proxy is working fine.
YES,
/// The proxy is down or not accepting connections.
NO,
/// The proxy was not tested.
UNTESTED
}
///
/// A proxy that supports http(s) and socks4/4a/5 protocols, authorization and chaining.
///
public class CProxy
{
/// Needed for NoSQL storage.
public Guid Id { get; set; }
/// The unparsed proxy string.
public string Proxy { get; set; } = "";
/// The username used for authentication (empty if none).
public string Username { get; set; } = "";
/// The password used for authentication (empty if none).
public string Password { get; set; } = "";
/// The type of proxy.
public ProxyType Type { get; set; } = ProxyType.Http;
/// The country of the proxy's ip.
public string Country { get; set; } = "";
/// The response delay of the proxy.
public int Ping { get; set; } = 0;
/// The next proxy object in a Proxy Chain.
public CProxy Next { get; set; } = null;
/// When the proxy was last used.
public DateTime LastUsed { get; set; } = new DateTime(1970, 1, 1);
/// When the proxy was last checked.
public DateTime LastChecked { get; set; } = new DateTime(1970, 1, 1);
/// The Working Status of the proxy.
public ProxyWorking Working { get; set; } = ProxyWorking.UNTESTED;
/// Whether the proxy has a successor in the Proxy Chain.
[BsonIgnore]
public bool HasNext { get { return Next != null; } }
/// The number of times the proxy was used for a check.
[BsonIgnore]
public int Uses { get; set; } = 0;
/// The number of bots the proxy is hooked to.
[BsonIgnore]
public int Hooked { get; set; } = 0;
/// The clearance cookie from Cloudflare.
[BsonIgnore]
public string Clearance { get; set; } = "";
/// The cfduid cookie from Cloudflare.
[BsonIgnore]
public string Cfduid { get; set; } = "";
/// The status of the proxy.
[BsonIgnore]
public Status Status { get; set; } = Status.AVAILABLE;
/// Needed for NoSQL deserialization.
public CProxy() { }
///
/// Creates a proxy given a string and a proxy type.
///
/// The unparsed proxy string
/// The proxy type
public CProxy(string proxy, ProxyType type)
{
Proxy = proxy;
Type = type;
}
///
/// Parses a CProxy object from an advanced string. Supports Proxy Chains.
///
/// The string to parse the proxy from
/// The default type to use when not specified
/// The parsed CProxy object
public CProxy Parse(string proxy, ProxyType defaultType = ProxyType.Http)
{
// 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(groups[1].Value, true, out var type);
Type = type;
current = current.Replace($"({groups[1].Value})", "");
}
else // Otherwise set the default type passed
{
Type = defaultType;
}
// 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], defaultType);
}
// Finally return this proxy
return this;
}
///
/// Gets the ProxyClient related to the specific proxy type.
///
/// The ProxyClient to be used in a HttpRequest
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;
}
}
}
/// The Host string parsed from the proxy.
[BsonIgnore]
public string Host
{
get
{
try { return Proxy.Split(':').First(); }
catch { return ""; }
}
}
/// The Port string parsed from the proxy.
[BsonIgnore]
public string Port
{
get
{
try { return Proxy.Split(':').Last(); }
catch { return ""; }
}
}
/// Whether the proxy is a valid numeric proxy.
[BsonIgnore]
public bool IsValidNumeric {
get {
return !(
Host == "" ||
Port == "" ||
Port.Any(c => !char.IsDigit(c)) ||
Host.Split('.').Count() != 4 ||
!IsNumeric
);
}
}
/// Whether the proxy is numeric.
[BsonIgnore]
public bool IsNumeric { get { return !Host.Split('.').Any(x => x.Any(c => !char.IsDigit(c))); } }
/// How long ago the proxy was used.
[BsonIgnore]
public TimeSpan UsedAgo { get { return DateTime.Now - LastUsed; } }
}
}