Compare commits

..

5 Commits

6 changed files with 178 additions and 23 deletions

View File

@ -1,5 +1,4 @@
global using Serilog;
global using Spectre.Console;
global using Newtonsoft.Json;
global using OF_DL;
global using OF_DL.CLI;
global using OF_DL.Enumerations;
@ -7,5 +6,13 @@ global using OF_DL.Exceptions;
global using OF_DL.Helpers;
global using OF_DL.Models;
global using OF_DL.Models.Config;
global using OF_DL.Models.Downloads;
global using OF_DL.Services;
global using Newtonsoft.Json;
global using Serilog;
global using Serilog.Context;
global using Spectre.Console;
global using MessageDtos = OF_DL.Models.Dtos.Messages;
global using MessageEntities = OF_DL.Models.Entities.Messages;
global using SubscriptionDtos = OF_DL.Models.Dtos.Subscriptions;
global using UserDtos = OF_DL.Models.Dtos.Users;
global using UserEntities = OF_DL.Models.Entities.Users;

View File

@ -1,8 +1,3 @@
using MessageDtos = OF_DL.Models.Dtos.Messages;
using MessageEntities = OF_DL.Models.Entities.Messages;
using UserDtos = OF_DL.Models.Dtos.Users;
using UserEntities = OF_DL.Models.Entities.Users;
namespace OF_DL.Services;
public class CajetanApiService(IAuthService authService, IConfigService configService, ICajetanDbService dbService, ICajetanDownloadEventHandler eventHandler)
@ -12,7 +7,7 @@ public class CajetanApiService(IAuthService authService, IConfigService configSe
public new async Task<UserEntities.User?> GetUserInfo(string endpoint)
{
UserEntities.UserInfo? userInfo = await GetDetailedUserInfo(endpoint);
UserEntities.UserInfo? userInfo = await GetDetailedUserInfoAsync(endpoint);
if (userInfo is not null && !endpoint.EndsWith("/me"))
await dbService.UpdateUserInfoAsync(userInfo);
@ -20,7 +15,7 @@ public class CajetanApiService(IAuthService authService, IConfigService configSe
return userInfo;
}
public async Task<UserEntities.UserInfo?> GetDetailedUserInfo(string endpoint)
public async Task<UserEntities.UserInfo?> GetDetailedUserInfoAsync(string endpoint)
{
Log.Debug($"Calling GetDetailedUserInfo: {endpoint}");
@ -57,6 +52,91 @@ public class CajetanApiService(IAuthService authService, IConfigService configSe
return null;
}
public async Task<Dictionary<string, long>> GetUsersWithProgressAsync(string typeDisplay, string endpoint, string? typeParam, bool offsetByCount)
{
Dictionary<string, long> usersOfType = await _eventHandler.WithStatusAsync(
statusMessage: $"Getting {typeDisplay} Users",
work: FetchAsync
);
return usersOfType;
async Task<Dictionary<string, long>> FetchAsync(IStatusReporter statusReporter)
{
Dictionary<string, long> users = [];
int limit = 50;
int offset = 0;
bool includeRestricted = true;
Dictionary<string, string> getParams = new()
{
["format"] = "infinite",
["limit"] = limit.ToString(),
["offset"] = offset.ToString()
};
if (!string.IsNullOrWhiteSpace(typeParam))
getParams["type"] = typeParam;
try
{
Log.Debug("Calling GetUsersWithProgress");
HttpClient client = GetHttpClient();
bool isLastLoop = false;
while (true)
{
string? body = await BuildHeaderAndExecuteRequests(getParams, endpoint, client);
if (string.IsNullOrWhiteSpace(body))
break;
SubscriptionDtos.SubscriptionsDto? subscriptions = DeserializeJson<SubscriptionDtos.SubscriptionsDto>(body, s_mJsonSerializerSettings);
if (subscriptions?.List is null)
break;
foreach (SubscriptionDtos.ListItemDto item in subscriptions.List)
{
if (string.IsNullOrWhiteSpace(item?.Username))
continue;
if (users.ContainsKey(item.Username))
continue;
bool isRestricted = item.IsRestricted ?? false;
bool isRestrictedButAllowed = isRestricted && includeRestricted;
if (!isRestricted || isRestrictedButAllowed)
users.Add(item.Username, item.Id);
}
statusReporter.ReportStatus($"[blue]Getting {typeDisplay} Users\n[/] [blue]Found {users.Count}[/]");
if (isLastLoop)
break;
if (!subscriptions.HasMore || subscriptions.List.Count == 0)
isLastLoop = true;
offset += offsetByCount
? subscriptions.List.Count
: limit;
getParams["offset"] = offset.ToString();
}
}
catch (Exception ex)
{
ExceptionLoggerHelper.LogException(ex);
}
return users;
}
}
public new async Task<MessageEntities.MessageCollection> GetMessages(string endpoint, string folder, IStatusReporter statusReporter)
{
(bool couldExtract, long userId) = ExtractUserId(endpoint);

View File

@ -1,5 +1,4 @@
using Microsoft.Data.Sqlite;
using OF_DL.Models.Entities.Users;
namespace OF_DL.Services;
@ -43,7 +42,7 @@ public class CajetanDbService(IConfigService configService)
return result;
}
public async Task UpdateUserInfoAsync(UserInfo? userInfo)
public async Task UpdateUserInfoAsync(UserEntities.UserInfo? userInfo)
{
if (userInfo?.Id is null || userInfo?.Username is null)
return;

View File

@ -1,11 +1,9 @@
using UserEntities = OF_DL.Models.Entities.Users;
namespace OF_DL.Services;
public interface ICajetanApiService : IApiService
{
Task<UserEntities.UserInfo?> GetDetailedUserInfo(string endpoint);
Task<UserEntities.UserInfo?> GetDetailedUserInfoAsync(string endpoint);
Task<Dictionary<string, long>> GetUsersWithProgressAsync(string typeDisplay, string endpoint, string? typeParam, bool offsetByCount);
Task<HashSet<long>> GetUsersWithUnreadMessagesAsync();
Task MarkAsUnreadAsync(string endpoint);
}

View File

@ -1,6 +1,3 @@
using OF_DL.Models.Entities.Users;
namespace OF_DL.Services;
public interface ICajetanDbService : IDbService
@ -8,5 +5,5 @@ public interface ICajetanDbService : IDbService
Task InitializeUserInfoTablesAsync();
Task<Dictionary<string, long>> GetUsersAsync();
Task UpdateUserInfoAsync(UserInfo? userInfo);
Task UpdateUserInfoAsync(UserEntities.UserInfo? userInfo);
}

View File

@ -1,6 +1,4 @@
using Microsoft.Extensions.DependencyInjection;
using OF_DL.Models.Downloads;
using OF_DL.Models.Entities.Users;
namespace OF_DL;
@ -115,7 +113,7 @@ internal class Worker(IServiceProvider serviceProvider)
// Validate cookie string
_authService.ValidateCookieString();
User? user = await _authService.ValidateAuthAsync();
UserEntities.User? user = await _authService.ValidateAuthAsync();
if (user is null || (user.Name is null && user.Username is null))
{
Log.Error("Auth failed");
@ -227,12 +225,88 @@ internal class Worker(IServiceProvider serviceProvider)
private async Task OutputBlockedUsersAsync()
{
const string OUTPUT_FILE_BLOCKED = "blocked-users.json";
const string OUTPUT_FILE_EXPIRED = "expired-users.json";
await GetUsersAsync("Blocked", "/users/blocked", OUTPUT_FILE_BLOCKED);
await GetUsersAsync("Expired", "/subscriptions/subscribes", OUTPUT_FILE_EXPIRED, typeParam: "expired", offsetByCount: false);
async Task GetUsersAsync(string typeDisplay, string uri, string outputFile, string? typeParam = null, bool offsetByCount = true)
{
Dictionary<string, long> users = await _apiService.GetUsersWithProgressAsync(typeDisplay, uri, typeParam, offsetByCount);
Console.WriteLine();
if (users is null || users.Count == 0)
{
AnsiConsole.Markup($"[green]No {typeDisplay} Users found.\n[/]");
}
else
{
AnsiConsole.Markup($"[green]Found {users.Count} {typeDisplay} Users, saving to '{outputFile}'\n[/]");
string json = JsonConvert.SerializeObject(users, Formatting.Indented);
await File.WriteAllTextAsync(outputFile, json);
}
}
}
private async Task UpdateUserInfoAsync()
{
await _dbService.CreateUsersDb([]);
await _dbService.InitializeUserInfoTablesAsync();
Dictionary<string, long> users = await _dbService.GetUsersAsync();
Console.WriteLine();
Log.Information("Updating User Info for '{UserCount}' users", users.Count);
AnsiConsole.Markup($"[green]Updating User Info for '{users.Count}' users\n[/]");
await AnsiConsole.Progress()
.Columns(new ProgressBarColumn(), new PercentageColumn(), new TaskDescriptionColumn { Alignment = Justify.Left })
.StartAsync(RunUpdateAsync);
async Task RunUpdateAsync(ProgressContext context)
{
ProgressTask? updateTask = null;
int maxUsernameLength = users.Keys.Max(s => s.Length);
foreach ((string username, long userId) in users)
{
string description = $"Updating '{username}'".PadRight(11 + maxUsernameLength);
double prevValue = updateTask?.Value ?? 0;
updateTask = context.AddTask(description, true, users.Count);
updateTask.Value = prevValue;
using (LogContext.PushProperty("Username", username))
using (LogContext.PushProperty("UserId", userId))
using (LogContext.PushProperty("UserNum", prevValue + 1))
using (LogContext.PushProperty("UserTotal", users.Count))
{
try
{
Log.Information("[{UserNum:0} of {UserTotal}] Updating User Info for for: {Username:l}");
UserEntities.UserInfo? userInfo = await _apiService.GetDetailedUserInfoAsync($"/users/{username}");
await _dbService.UpdateUserInfoAsync(userInfo);
updateTask.Description = $"{description} - COMPLETE";
}
catch (Exception ex)
{
Log.Warning(ex, "[{UserNum:0} of {UserTotal}] Failed to update User Info for: {Username:l}");
AnsiConsole.Markup($"[red]Failed to update User Info for '{username}'\n[/]");
updateTask.Description = $"{description} - FAILED: {ex.Message}";
}
finally
{
updateTask.Increment(1);
updateTask.StopTask();
}
}
}
}
}
private async Task<Dictionary<string, long>> GetUsersFromSpecificListsAsync(UserListResult allUsersAndLists, string[] listNames)