mirror of
https://github.com/barelyprofessional/KfChatDotNet.git
synced 2026-05-02 04:22:04 -04:00
Added Rainbet bet history scraping. Uses FlareSolverr to grab Cloudflare cookies then retrieves from the bet feed. Not perfect but mostly works.
This commit is contained in:
120
KfChatDotNetBot/Services/Rainbet.cs
Normal file
120
KfChatDotNetBot/Services/Rainbet.cs
Normal file
@@ -0,0 +1,120 @@
|
||||
using System.Net;
|
||||
using System.Net.Http.Json;
|
||||
using FlareSolverrSharp;
|
||||
using FlareSolverrSharp.Exceptions;
|
||||
using KfChatDotNetBot.Models;
|
||||
using KfChatDotNetBot.Settings;
|
||||
using NLog;
|
||||
|
||||
namespace KfChatDotNetBot.Services;
|
||||
|
||||
public class Rainbet : IDisposable
|
||||
{
|
||||
private Logger _logger = LogManager.GetCurrentClassLogger();
|
||||
public delegate void OnRainbetBetEventHandler(object sender, List<RainbetBetHistoryModel> bets);
|
||||
public event OnRainbetBetEventHandler OnRainbetBet;
|
||||
private CancellationToken _cancellationToken = CancellationToken.None;
|
||||
private CancellationTokenSource _gameHistoryCts = new();
|
||||
private Task? _gameHistoryTask;
|
||||
private TimeSpan _gameHistoryInterval = TimeSpan.FromSeconds(60);
|
||||
|
||||
public Rainbet(CancellationToken? cancellationToken = null)
|
||||
{
|
||||
if (cancellationToken != null) _cancellationToken = cancellationToken.Value;
|
||||
_logger.Info("Rainbet client created");
|
||||
}
|
||||
|
||||
public void StartGameHistoryTimer()
|
||||
{
|
||||
_gameHistoryTask = GameHistoryTimer();
|
||||
}
|
||||
|
||||
private async Task GameHistoryTimer()
|
||||
{
|
||||
using var timer = new PeriodicTimer(_gameHistoryInterval);
|
||||
while (await timer.WaitForNextTickAsync(_gameHistoryCts.Token))
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.Info("Retrieving game history from Rainbet");
|
||||
var bets = await GetGameHistory(1000);
|
||||
OnRainbetBet?.Invoke(this, bets);
|
||||
}
|
||||
catch (FlareSolverrException e)
|
||||
{
|
||||
_logger.Error("Caught a FlareSolverrException, probably that retarded cookie bug that has been unfixed for 3+ years");
|
||||
_logger.Error("Trying again immediately as it's pretty rare it happens twice in a row");
|
||||
_logger.Error(e);
|
||||
try
|
||||
{
|
||||
var bets = await GetGameHistory(1000);
|
||||
OnRainbetBet?.Invoke(this, bets);
|
||||
}
|
||||
catch (Exception ee)
|
||||
{
|
||||
_logger.Error("Fuck my life, failed again. We'll just wait until the next tick");
|
||||
_logger.Error(ee);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.Error("Caught error when retrieving bets and invoking the event");
|
||||
_logger.Error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FlareSolverr C# client does not support POSTing application/json so this method involves
|
||||
// 1. Getting the home page (as you're unlikely to get a CF challenge checkbox. Probably due to some config to limit
|
||||
// friction for degens on VPNs, which is like 99% of the traffic for these shit casinos)
|
||||
// 2. Using the cookies from that request to do the actual POST
|
||||
// Cookies and UA must match or the trannies at Cloudflare will reject your cookies
|
||||
// take = 10 is the default, but it can go higher
|
||||
public async Task<List<RainbetBetHistoryModel>> GetGameHistory(int take = 10)
|
||||
{
|
||||
var settings =
|
||||
await Helpers.GetMultipleValues([BuiltIn.Keys.FlareSolverrApiUrl, BuiltIn.Keys.FlareSolverrProxy]);
|
||||
var flareSolverrUrl = settings[BuiltIn.Keys.FlareSolverrApiUrl];
|
||||
var flareSolverrProxy = settings[BuiltIn.Keys.FlareSolverrProxy];
|
||||
var handler = new ClearanceHandler(flareSolverrUrl.Value)
|
||||
{
|
||||
// Generally takes <5 seconds
|
||||
MaxTimeout = 30000,
|
||||
};
|
||||
_logger.Debug($"Configured clearance handler to use FlareSolverr endpoint: {flareSolverrUrl.Value}");
|
||||
// I would suggest not using a proxy. It's pretty much a miracle this works at all.
|
||||
if (flareSolverrProxy.Value != null)
|
||||
{
|
||||
handler.ProxyUrl = flareSolverrProxy.Value;
|
||||
_logger.Debug($"Configured clearance handler to use {flareSolverrProxy.Value} for proxying the request");
|
||||
}
|
||||
var gameHistoryUrl = "https://sportsbook.rainbet.com/v1/game-history";
|
||||
var client = new HttpClient(handler);
|
||||
var jsonBody = new Dictionary<string, int> { {"take", take} };
|
||||
var postData = JsonContent.Create(jsonBody);
|
||||
// You get CF checkbox'd if you go directly to sportsbook.rainbet.com but works ok for root
|
||||
var getResponse = await client.GetAsync("https://rainbet.com/", _cancellationToken);
|
||||
var postClientHandler = new HttpClientHandler();
|
||||
if (flareSolverrProxy.Value != null)
|
||||
{
|
||||
postClientHandler.Proxy = new WebProxy(flareSolverrProxy.Value);
|
||||
postClientHandler.UseProxy = true;
|
||||
_logger.Debug($"Configured API request to use {flareSolverrProxy.Value}");
|
||||
}
|
||||
var postClient = new HttpClient(postClientHandler);
|
||||
postClient.DefaultRequestHeaders.Add("Cookie", getResponse.Headers.GetValues("Set-Cookie"));
|
||||
postClient.DefaultRequestHeaders.UserAgent.Clear();
|
||||
postClient.DefaultRequestHeaders.UserAgent.ParseAdd(getResponse.RequestMessage.Headers.UserAgent.ToString());
|
||||
var response = await postClient.PostAsync(gameHistoryUrl, postData, _cancellationToken);
|
||||
var bets = await response.Content.ReadFromJsonAsync<List<RainbetBetHistoryModel>>(cancellationToken: _cancellationToken);
|
||||
return bets;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_gameHistoryCts.Cancel();
|
||||
_gameHistoryCts.Dispose();
|
||||
_gameHistoryTask?.Dispose();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
@@ -207,7 +207,16 @@ public class Shuffle : IDisposable
|
||||
public void Dispose()
|
||||
{
|
||||
_wsClient.Dispose();
|
||||
_pingCts.Cancel();
|
||||
// Rare bug but has happened at least once
|
||||
try
|
||||
{
|
||||
_pingCts.Cancel();
|
||||
}
|
||||
catch (ObjectDisposedException e)
|
||||
{
|
||||
_logger.Error("Caught object disposed exception when trying to send a cancellation to the ping task");
|
||||
_logger.Error(e);
|
||||
}
|
||||
_pingCts.Dispose();
|
||||
_pingTask.Dispose();
|
||||
GC.SuppressFinalize(this);
|
||||
|
||||
Reference in New Issue
Block a user