171 lines
5.9 KiB
C#
171 lines
5.9 KiB
C#
using OF_DL.Entities;
|
|
using Serilog;
|
|
using Microsoft.Playwright;
|
|
|
|
namespace OF_DL.Helpers;
|
|
|
|
public class AuthHelper
|
|
{
|
|
private readonly string _userDataDir = Path.GetFullPath("chromium-data");
|
|
|
|
private const string _initScriptsDirName = "chromium-scripts";
|
|
|
|
private readonly BrowserTypeLaunchPersistentContextOptions _options = new()
|
|
{
|
|
Headless = false,
|
|
Channel = "chromium",
|
|
Args = ["--no-sandbox", "--disable-setuid-sandbox", "--disable-blink-features=AutomationControlled", "--disable-infobars"],
|
|
};
|
|
|
|
private readonly string[] _desiredCookies =
|
|
[
|
|
"auth_id",
|
|
"sess"
|
|
];
|
|
|
|
private const float LoginTimeout = 600000f; // 10 minutes
|
|
private const float FeedLoadTimeout = 60000f; // 1 minute
|
|
|
|
public async Task SetupBrowser(bool runningInDocker)
|
|
{
|
|
if (runningInDocker)
|
|
{
|
|
Log.Information("Running in Docker. Disabling sandbox and GPU.");
|
|
_options.Args = ["--no-sandbox", "--disable-setuid-sandbox", "--disable-gpu", "--disable-blink-features=AutomationControlled", "--disable-infobars"];
|
|
}
|
|
else
|
|
{
|
|
var exitCode = Microsoft.Playwright.Program.Main(new[] {"install", "--with-deps", "chromium"});
|
|
if (exitCode != 0)
|
|
{
|
|
throw new Exception($"Playwright chromium download failed. Exited with code {exitCode}");
|
|
}
|
|
}
|
|
}
|
|
|
|
private async Task<string> GetBcToken(IPage page)
|
|
{
|
|
return await page.EvaluateAsync<string>("window.localStorage.getItem('bcTokenSha') || ''");
|
|
}
|
|
|
|
public async Task<Auth?> GetAuthFromBrowser(bool isDocker = false)
|
|
{
|
|
try
|
|
{
|
|
IBrowserContext? browser = null;
|
|
try
|
|
{
|
|
var playwright = await Playwright.CreateAsync();
|
|
browser = await playwright.Chromium.LaunchPersistentContextAsync(_userDataDir, _options);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
if (e.Message.Contains("An error occurred trying to start process") && Directory.Exists(_userDataDir))
|
|
{
|
|
Log.Error("Failed to launch browser. Deleting chrome-data directory and trying again.");
|
|
Directory.Delete(_userDataDir, true);
|
|
IPlaywright playwright = await Playwright.CreateAsync();
|
|
browser = await playwright.Chromium.LaunchPersistentContextAsync(_userDataDir, _options);
|
|
}
|
|
else
|
|
{
|
|
throw;
|
|
}
|
|
}
|
|
|
|
if (browser == null)
|
|
{
|
|
throw new Exception("Could not get browser");
|
|
}
|
|
|
|
IPage? page = browser.Pages[0];
|
|
|
|
if (page == null)
|
|
{
|
|
throw new Exception("Could not get page");
|
|
}
|
|
|
|
string exeDirectory = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location) ?? string.Empty;
|
|
string initScriptsDir = Path.Combine(exeDirectory, _initScriptsDirName);
|
|
if (Directory.Exists(initScriptsDir))
|
|
{
|
|
Log.Information("Loading init scripts from {initScriptsDir}", initScriptsDir);
|
|
foreach (string initScript in Directory.GetFiles(initScriptsDir, "*.js"))
|
|
{
|
|
Log.Debug("Loading init script {initScript}", initScript);
|
|
await page.AddInitScriptAsync(initScript);
|
|
}
|
|
}
|
|
|
|
Log.Debug("Navigating to OnlyFans.");
|
|
await page.GotoAsync("https://onlyfans.com");
|
|
|
|
Log.Debug("Waiting for user to login");
|
|
await page.WaitForSelectorAsync(".b-feed", new PageWaitForSelectorOptions { Timeout = LoginTimeout });
|
|
Log.Debug("Feed element detected (user logged in)");
|
|
|
|
await page.ReloadAsync();
|
|
|
|
await page.WaitForNavigationAsync(new PageWaitForNavigationOptions {
|
|
WaitUntil = WaitUntilState.DOMContentLoaded,
|
|
Timeout = FeedLoadTimeout
|
|
});
|
|
Log.Debug("DOM loaded. Getting BC token and cookies ...");
|
|
|
|
string xBc;
|
|
try
|
|
{
|
|
xBc = await GetBcToken(page);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
await browser.CloseAsync();
|
|
throw new Exception("Error getting bcToken");
|
|
}
|
|
|
|
Dictionary<string, string> mappedCookies = (await browser.CookiesAsync())
|
|
.Where(cookie => cookie.Domain.Contains("onlyfans.com"))
|
|
.ToDictionary(cookie => cookie.Name, cookie => cookie.Value);
|
|
|
|
mappedCookies.TryGetValue("auth_id", out string? userId);
|
|
if (userId == null)
|
|
{
|
|
await browser.CloseAsync();
|
|
throw new Exception("Could not find 'auth_id' cookie");
|
|
}
|
|
|
|
mappedCookies.TryGetValue("sess", out string? sess);
|
|
if (sess == null)
|
|
{
|
|
await browser.CloseAsync();
|
|
throw new Exception("Could not find 'sess' cookie");
|
|
}
|
|
|
|
string? userAgent = await page.EvaluateAsync<string>("navigator.userAgent");
|
|
if (userAgent == null)
|
|
{
|
|
await browser.CloseAsync();
|
|
throw new Exception("Could not get user agent");
|
|
}
|
|
|
|
string cookies = string.Join(" ", mappedCookies.Keys.Where(key => _desiredCookies.Contains(key))
|
|
.Select(key => $"${key}={mappedCookies[key]};"));
|
|
|
|
await browser.CloseAsync();
|
|
|
|
return new Auth()
|
|
{
|
|
COOKIE = cookies,
|
|
USER_AGENT = userAgent,
|
|
USER_ID = userId,
|
|
X_BC = xBc
|
|
};
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Log.Error(e, "Error getting auth from browser");
|
|
return null;
|
|
}
|
|
}
|
|
}
|