using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
namespace RuriLib.Models
{
///
/// Class that manages a pool of proxies.
///
public class ProxyPool
{
private static Random rng = new Random();
/// The full list of proxies in the pool.
public List Proxies { get; } = new List();
/// The list of Alive proxies (AVAILABLE or BUSY).
public List Alive { get { return Proxies.Where(p => p.Status == Status.AVAILABLE || p.Status == Status.BUSY).ToList(); } }
/// The list of Available proxies.
public List Available { get { return Proxies.Where(p => p.Status == Status.AVAILABLE).ToList(); } }
/// The list of Banned proxies.
public List Banned { get { return Proxies.Where(p => p.Status == Status.BANNED).ToList(); } }
/// The list of Bad proxies.
public List Bad { get { return Proxies.Where(p => p.Status == Status.BAD).ToList(); } }
///
/// Whether the proxy list is locked or not.
/// It prevents multiple bots from choosing the same proxy simultaneously, regardless of the fact that concurrent usage may be disabled.
///
private bool Locked { get; set; } = false;
///
/// Initializes the proxy pool given a collection of string to be parsed as proxies and their type.
///
/// The collection of strings to parse the proxies from
/// The type of the proxies
/// Whether to shuffle the proxy list
public ProxyPool(IEnumerable proxies, Extreme.Net.ProxyType type, bool shuffle)
{
Proxies = proxies.Select(p => new CProxy(p, type)).ToList();
if (shuffle) Shuffle(Proxies);
}
///
/// Initializes the proxy pool given a collection of CProxy objects.
/// The proxies will be first cloned and then stored in the list.
/// They will also be unbanned to make sure there are no leftovers from previous checks.
///
/// The list of CProxy objects to be cloned and added to the list
/// Whether to shuffle the proxy list
public ProxyPool(List proxies, bool shuffle = false)
{
// 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();
// Shuffle it if necessary
if (shuffle) Shuffle(Proxies);
}
///
/// Clears the Clearance and Cfduid cookies set by Cloudflare.
///
public void ClearCF()
{
foreach (var proxy in Proxies)
{
proxy.Clearance = "";
proxy.Cfduid = "";
}
}
///
/// Sets all the BANNED and BAD proxies status to AVAILABLE and resets their Uses. It also clears the Cloudflare cookies.
///
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();
}
///
/// Tries to return the first available proxy from the list.
///
/// Whether to include proxies that are being used by other bots
/// The maximum uses of a proxy, after which it will be banned (0 for infinite)
/// Whether a proxy can ever be banned
/// The first available proxy respecting the conditions or null
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;
}
private static void Shuffle(IList list)
{
int n = list.Count;
while (n > 1)
{
n--;
int k = rng.Next(n + 1);
T value = list[k];
list[k] = list[n];
list[n] = value;
}
}
}
}