forked from sim0n00ps/OF-DL
Compare commits
3 Commits
7042afc76a
...
e6a0e9f894
| Author | SHA1 | Date | |
|---|---|---|---|
| e6a0e9f894 | |||
| 120e09df8c | |||
| 0f277c05ad |
@ -5,7 +5,5 @@ public class CajetanConfig
|
|||||||
public string[] NonInteractiveSpecificLists { get; set; } = [];
|
public string[] NonInteractiveSpecificLists { get; set; } = [];
|
||||||
public string[] NonInteractiveSpecificUsers { get; set; } = [];
|
public string[] NonInteractiveSpecificUsers { get; set; } = [];
|
||||||
|
|
||||||
public EMode Mode { get; set; } = EMode.None;
|
public EMode Mode { get; set; } = EMode.DownloadCreatorContent;
|
||||||
|
|
||||||
public string ErrorMessage { get; set; } = string.Empty;
|
|
||||||
}
|
}
|
||||||
|
|||||||
8
Cajetan.OF-DL/Models/Dtos/Lists/ListUsersDto.cs
Normal file
8
Cajetan.OF-DL/Models/Dtos/Lists/ListUsersDto.cs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
namespace OF_DL.Models.Dtos.Lists;
|
||||||
|
|
||||||
|
public class ListUsersDto
|
||||||
|
{
|
||||||
|
[JsonProperty("list")] public List<UsersListDto> List { get; set; } = [];
|
||||||
|
[JsonProperty("hasMore")] public bool? HasMore { get; set; }
|
||||||
|
[JsonProperty("nextOffset")] public int NextOffset { get; set; }
|
||||||
|
}
|
||||||
@ -2,7 +2,6 @@ namespace OF_DL.Models;
|
|||||||
|
|
||||||
public enum EMode
|
public enum EMode
|
||||||
{
|
{
|
||||||
None,
|
|
||||||
DownloadCreatorContent,
|
DownloadCreatorContent,
|
||||||
OutputBlockedUsers,
|
OutputBlockedUsers,
|
||||||
UpdateAllUserInfo
|
UpdateAllUserInfo
|
||||||
|
|||||||
@ -49,14 +49,7 @@ static async Task<ServiceCollection> ConfigureServices(string[] args)
|
|||||||
|
|
||||||
AnsiConsole.Markup("[green]config.conf located successfully!\n[/]");
|
AnsiConsole.Markup("[green]config.conf located successfully!\n[/]");
|
||||||
|
|
||||||
if (!ParseCommandlineArgs(args, configService.CurrentConfig, out CajetanConfig cajetanConfig))
|
CajetanConfig cajetanConfig = ParseCommandlineArgs(args, configService.CurrentConfig);
|
||||||
{
|
|
||||||
AnsiConsole.MarkupLine($"\n[red]{cajetanConfig.ErrorMessage}[/]\n");
|
|
||||||
AnsiConsole.MarkupLine("[red]Press any key to exit.[/]");
|
|
||||||
Console.ReadKey();
|
|
||||||
|
|
||||||
exitHelper.ExitWithCode(3);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set up full dependency injection with loaded config
|
// Set up full dependency injection with loaded config
|
||||||
services = [];
|
services = [];
|
||||||
@ -90,26 +83,26 @@ static async Task<ServiceCollection> ConfigureServices(string[] args)
|
|||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool ParseCommandlineArgs(string[] args, Config currentConfig, out CajetanConfig parsedConfig)
|
static CajetanConfig ParseCommandlineArgs(string[] args, Config currentConfig)
|
||||||
{
|
{
|
||||||
const string SPECIFIC_LISTS_ARG = "--specific-lists";
|
const string SPECIFIC_LISTS_ARG = "--specific-lists";
|
||||||
const string SPECIFIC_USERS_ARG = "--specific-users";
|
const string SPECIFIC_USERS_ARG = "--specific-users";
|
||||||
const string OUTPUT_BLOCKED_USERS_ARG = "--output-blocked";
|
const string OUTPUT_BLOCKED_USERS_ARG = "--output-blocked";
|
||||||
const string UPDATE_ALL_USER_INFO_ARG = "--update-userinfo";
|
const string UPDATE_ALL_USER_INFO_ARG = "--update-userinfo";
|
||||||
|
|
||||||
parsedConfig = new();
|
CajetanConfig parsedConfig = new();
|
||||||
|
|
||||||
if (ParseListAndUserArguments(ref parsedConfig))
|
if (ParseListAndUserArguments(ref parsedConfig))
|
||||||
return true;
|
return parsedConfig;
|
||||||
|
|
||||||
if (ParseFlagArgument(OUTPUT_BLOCKED_USERS_ARG, EMode.OutputBlockedUsers, ref parsedConfig))
|
if (ParseFlagArgument(OUTPUT_BLOCKED_USERS_ARG, EMode.OutputBlockedUsers, ref parsedConfig))
|
||||||
return true;
|
return parsedConfig;
|
||||||
|
|
||||||
if (ParseFlagArgument(UPDATE_ALL_USER_INFO_ARG, EMode.UpdateAllUserInfo, ref parsedConfig))
|
if (ParseFlagArgument(UPDATE_ALL_USER_INFO_ARG, EMode.UpdateAllUserInfo, ref parsedConfig))
|
||||||
return true;
|
return parsedConfig;
|
||||||
|
|
||||||
parsedConfig.ErrorMessage = "No mode argument provided!";
|
// Will process all active subscriptions
|
||||||
return false;
|
return parsedConfig;
|
||||||
|
|
||||||
bool ParseListAndUserArguments(ref CajetanConfig parsedConfig)
|
bool ParseListAndUserArguments(ref CajetanConfig parsedConfig)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -11,6 +11,7 @@ global using OF_DL.Services;
|
|||||||
global using Serilog;
|
global using Serilog;
|
||||||
global using Serilog.Context;
|
global using Serilog.Context;
|
||||||
global using Spectre.Console;
|
global using Spectre.Console;
|
||||||
|
global using ListsDtos = OF_DL.Models.Dtos.Lists;
|
||||||
global using MessageDtos = OF_DL.Models.Dtos.Messages;
|
global using MessageDtos = OF_DL.Models.Dtos.Messages;
|
||||||
global using MessageEntities = OF_DL.Models.Entities.Messages;
|
global using MessageEntities = OF_DL.Models.Entities.Messages;
|
||||||
global using SubscriptionsDtos = OF_DL.Models.Dtos.Subscriptions;
|
global using SubscriptionsDtos = OF_DL.Models.Dtos.Subscriptions;
|
||||||
|
|||||||
@ -27,6 +27,95 @@ public class CajetanApiService(IAuthService authService, IConfigService configSe
|
|||||||
return GetAllSubscriptions(endpoint, includeRestricted, "expired");
|
return GetAllSubscriptions(endpoint, includeRestricted, "expired");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public new async Task<MessageEntities.MessageCollection> GetMessages(string endpoint, string folder, IStatusReporter statusReporter)
|
||||||
|
{
|
||||||
|
(bool couldExtract, long userId) = ExtractUserId(endpoint);
|
||||||
|
|
||||||
|
_eventHandler.OnMessage("Getting Unread Chats", "grey");
|
||||||
|
HashSet<long> usersWithUnread = couldExtract ? await GetUsersWithUnreadMessagesAsync() : [];
|
||||||
|
|
||||||
|
MessageEntities.MessageCollection messages = await base.GetMessages(endpoint, folder, statusReporter);
|
||||||
|
|
||||||
|
if (usersWithUnread.Contains(userId))
|
||||||
|
{
|
||||||
|
_eventHandler.OnMessage("Restoring unread state", "grey");
|
||||||
|
await MarkAsUnreadAsync($"/chats/{userId}/mark-as-read");
|
||||||
|
}
|
||||||
|
|
||||||
|
return messages;
|
||||||
|
|
||||||
|
static (bool couldExtract, long userId) ExtractUserId(string endpoint)
|
||||||
|
{
|
||||||
|
string withoutChatsAndMessages = endpoint
|
||||||
|
.Replace("chats", "", StringComparison.OrdinalIgnoreCase)
|
||||||
|
.Replace("messages", "", StringComparison.OrdinalIgnoreCase);
|
||||||
|
string trimmed = withoutChatsAndMessages.Trim(' ', '/', '\\');
|
||||||
|
|
||||||
|
if (long.TryParse(trimmed, out long userId))
|
||||||
|
return (true, userId);
|
||||||
|
|
||||||
|
return (false, default);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public new async Task<Dictionary<string, long>?> GetListUsers(string endpoint)
|
||||||
|
{
|
||||||
|
if (!HasSignedRequestAuth())
|
||||||
|
return null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Dictionary<string, long> users = new(StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
Log.Debug($"Calling GetListUsers - {endpoint}");
|
||||||
|
|
||||||
|
const int limit = 50;
|
||||||
|
int offset = 0;
|
||||||
|
|
||||||
|
Dictionary<string, string> getParams = new()
|
||||||
|
{
|
||||||
|
["format"] = "infinite",
|
||||||
|
["limit"] = limit.ToString()
|
||||||
|
};
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
getParams["offset"] = offset.ToString();
|
||||||
|
|
||||||
|
string? body = await BuildHeaderAndExecuteRequests(getParams, endpoint, new HttpClient());
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(body))
|
||||||
|
break;
|
||||||
|
|
||||||
|
ListsDtos.ListUsersDto? listUsers = DeserializeJson<ListsDtos.ListUsersDto>(body, s_mJsonSerializerSettings);
|
||||||
|
|
||||||
|
if (listUsers?.List is null)
|
||||||
|
break;
|
||||||
|
|
||||||
|
foreach (ListsDtos.UsersListDto item in listUsers.List)
|
||||||
|
{
|
||||||
|
if (item.Id is null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
users.TryAdd(item.Username, item.Id.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (listUsers.HasMore is false)
|
||||||
|
break;
|
||||||
|
|
||||||
|
offset = listUsers.NextOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
return users;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
ExceptionLoggerHelper.LogException(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<UserEntities.UserInfo?> GetDetailedUserInfoAsync(string endpoint)
|
public async Task<UserEntities.UserInfo?> GetDetailedUserInfoAsync(string endpoint)
|
||||||
{
|
{
|
||||||
Log.Debug($"Calling GetDetailedUserInfo: {endpoint}");
|
Log.Debug($"Calling GetDetailedUserInfo: {endpoint}");
|
||||||
@ -64,29 +153,6 @@ public class CajetanApiService(IAuthService authService, IConfigService configSe
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task SortBlockedAsync(string endpoint, string order = "recent", string direction = "desc")
|
|
||||||
{
|
|
||||||
Log.Debug($"Calling SortBlocked - {endpoint}");
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var reqBody = new { order, direction };
|
|
||||||
var result = new { success = false, canAddFriends = false };
|
|
||||||
|
|
||||||
string? body = await BuildHeaderAndExecuteRequests([], endpoint, GetHttpClient(), HttpMethod.Post, reqBody);
|
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(body))
|
|
||||||
result = JsonConvert.DeserializeAnonymousType(body, result);
|
|
||||||
|
|
||||||
if (result?.success != true)
|
|
||||||
_eventHandler.OnMessage($"Failed to sort blocked (order: {order}, direction; {direction})! Endpoint: {endpoint}", "yellow");
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
ExceptionLoggerHelper.LogException(ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<Dictionary<string, long>> GetUsersWithProgressAsync(string typeDisplay, string endpoint, string? typeParam, bool offsetByCount)
|
public async Task<Dictionary<string, long>> GetUsersWithProgressAsync(string typeDisplay, string endpoint, string? typeParam, bool offsetByCount)
|
||||||
{
|
{
|
||||||
Dictionary<string, long> usersOfType = await _eventHandler.WithStatusAsync(
|
Dictionary<string, long> usersOfType = await _eventHandler.WithStatusAsync(
|
||||||
@ -172,37 +238,6 @@ public class CajetanApiService(IAuthService authService, IConfigService configSe
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public new async Task<MessageEntities.MessageCollection> GetMessages(string endpoint, string folder, IStatusReporter statusReporter)
|
|
||||||
{
|
|
||||||
(bool couldExtract, long userId) = ExtractUserId(endpoint);
|
|
||||||
|
|
||||||
_eventHandler.OnMessage("Getting Unread Chats", "grey");
|
|
||||||
HashSet<long> usersWithUnread = couldExtract ? await GetUsersWithUnreadMessagesAsync() : [];
|
|
||||||
|
|
||||||
MessageEntities.MessageCollection messages = await base.GetMessages(endpoint, folder, statusReporter);
|
|
||||||
|
|
||||||
if (usersWithUnread.Contains(userId))
|
|
||||||
{
|
|
||||||
_eventHandler.OnMessage("Restoring unread state", "grey");
|
|
||||||
await MarkAsUnreadAsync($"/chats/{userId}/mark-as-read");
|
|
||||||
}
|
|
||||||
|
|
||||||
return messages;
|
|
||||||
|
|
||||||
static (bool couldExtract, long userId) ExtractUserId(string endpoint)
|
|
||||||
{
|
|
||||||
string withoutChatsAndMessages = endpoint
|
|
||||||
.Replace("chats", "", StringComparison.OrdinalIgnoreCase)
|
|
||||||
.Replace("messages", "", StringComparison.OrdinalIgnoreCase);
|
|
||||||
string trimmed = withoutChatsAndMessages.Trim(' ', '/', '\\');
|
|
||||||
|
|
||||||
if (long.TryParse(trimmed, out long userId))
|
|
||||||
return (true, userId);
|
|
||||||
|
|
||||||
return (false, default);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<HashSet<long>> GetUsersWithUnreadMessagesAsync()
|
public async Task<HashSet<long>> GetUsersWithUnreadMessagesAsync()
|
||||||
{
|
{
|
||||||
MessageDtos.ChatsDto unreadChats = await GetChatsAsync("/chats", onlyUnread: true);
|
MessageDtos.ChatsDto unreadChats = await GetChatsAsync("/chats", onlyUnread: true);
|
||||||
@ -244,6 +279,29 @@ public class CajetanApiService(IAuthService authService, IConfigService configSe
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task SortBlockedAsync(string endpoint, string order = "recent", string direction = "desc")
|
||||||
|
{
|
||||||
|
Log.Debug($"Calling SortBlocked - {endpoint}");
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var reqBody = new { order, direction };
|
||||||
|
var result = new { success = false, canAddFriends = false };
|
||||||
|
|
||||||
|
string? body = await BuildHeaderAndExecuteRequests([], endpoint, GetHttpClient(), HttpMethod.Post, reqBody);
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(body))
|
||||||
|
result = JsonConvert.DeserializeAnonymousType(body, result);
|
||||||
|
|
||||||
|
if (result?.success != true)
|
||||||
|
_eventHandler.OnMessage($"Failed to sort blocked (order: {order}, direction; {direction})! Endpoint: {endpoint}", "yellow");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
ExceptionLoggerHelper.LogException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async Task<Dictionary<string, long>?> GetAllSubscriptions(string endpoint, bool includeRestricted, string type)
|
private async Task<Dictionary<string, long>?> GetAllSubscriptions(string endpoint, bool includeRestricted, string type)
|
||||||
{
|
{
|
||||||
if (!HasSignedRequestAuth())
|
if (!HasSignedRequestAuth())
|
||||||
|
|||||||
@ -2,6 +2,8 @@ namespace OF_DL.Services;
|
|||||||
|
|
||||||
public interface ICajetanApiService : IApiService
|
public interface ICajetanApiService : IApiService
|
||||||
{
|
{
|
||||||
|
new Task<Dictionary<string, long>?> GetListUsers(string endpoint);
|
||||||
|
|
||||||
Task<UserEntities.UserInfo?> GetDetailedUserInfoAsync(string endpoint);
|
Task<UserEntities.UserInfo?> GetDetailedUserInfoAsync(string endpoint);
|
||||||
Task<Dictionary<string, long>> GetUsersWithProgressAsync(string typeDisplay, string endpoint, string? typeParam, bool offsetByCount);
|
Task<Dictionary<string, long>> GetUsersWithProgressAsync(string typeDisplay, string endpoint, string? typeParam, bool offsetByCount);
|
||||||
Task<HashSet<long>> GetUsersWithUnreadMessagesAsync();
|
Task<HashSet<long>> GetUsersWithUnreadMessagesAsync();
|
||||||
|
|||||||
@ -134,7 +134,7 @@ internal class Worker(IServiceProvider serviceProvider)
|
|||||||
DateTime startTime = DateTime.Now;
|
DateTime startTime = DateTime.Now;
|
||||||
|
|
||||||
UserListResult allUsersAndLists = await GetAvailableUsersAsync();
|
UserListResult allUsersAndLists = await GetAvailableUsersAsync();
|
||||||
Dictionary<string, long> usersToDownload = [];
|
Dictionary<string, long> usersToDownload = allUsersAndLists.Users;
|
||||||
|
|
||||||
if (_cajetanConfig.NonInteractiveSpecificLists is not null && _cajetanConfig.NonInteractiveSpecificLists.Length > 0)
|
if (_cajetanConfig.NonInteractiveSpecificLists is not null && _cajetanConfig.NonInteractiveSpecificLists.Length > 0)
|
||||||
usersToDownload = await GetUsersFromSpecificListsAsync(allUsersAndLists, _cajetanConfig.NonInteractiveSpecificLists);
|
usersToDownload = await GetUsersFromSpecificListsAsync(allUsersAndLists, _cajetanConfig.NonInteractiveSpecificLists);
|
||||||
@ -142,9 +142,6 @@ internal class Worker(IServiceProvider serviceProvider)
|
|||||||
else if (_cajetanConfig.NonInteractiveSpecificUsers is not null && _cajetanConfig.NonInteractiveSpecificUsers.Length > 0)
|
else if (_cajetanConfig.NonInteractiveSpecificUsers is not null && _cajetanConfig.NonInteractiveSpecificUsers.Length > 0)
|
||||||
usersToDownload = GetUsersFromSpecificUsernames(allUsersAndLists, [.. _cajetanConfig.NonInteractiveSpecificUsers]);
|
usersToDownload = GetUsersFromSpecificUsernames(allUsersAndLists, [.. _cajetanConfig.NonInteractiveSpecificUsers]);
|
||||||
|
|
||||||
if (usersToDownload.Count == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
int userNum = 0;
|
int userNum = 0;
|
||||||
int userCount = usersToDownload.Count;
|
int userCount = usersToDownload.Count;
|
||||||
CajetanDownloadEventHandler eventHandler = new();
|
CajetanDownloadEventHandler eventHandler = new();
|
||||||
@ -328,17 +325,14 @@ internal class Worker(IServiceProvider serviceProvider)
|
|||||||
Log.Information("Getting Users from list '{ListName:l}' (Include Restricted: {IncludeRestrictedSubscriptions})", name, currentConfig.IncludeRestrictedSubscriptions);
|
Log.Information("Getting Users from list '{ListName:l}' (Include Restricted: {IncludeRestrictedSubscriptions})", name, currentConfig.IncludeRestrictedSubscriptions);
|
||||||
AnsiConsole.MarkupLine($"[green]Getting Users from list '{name}' (Include Restricted: {currentConfig.IncludeRestrictedSubscriptions})[/]");
|
AnsiConsole.MarkupLine($"[green]Getting Users from list '{name}' (Include Restricted: {currentConfig.IncludeRestrictedSubscriptions})[/]");
|
||||||
|
|
||||||
List<string> listUsernames = await _apiService.GetListUsers($"/lists/{listId}/users") ?? [];
|
Dictionary<string, long> listUsernames = await _apiService.GetListUsers($"/lists/{listId}/users") ?? [];
|
||||||
|
|
||||||
foreach (string u in listUsernames)
|
foreach ((string username, long userId) in listUsernames)
|
||||||
{
|
{
|
||||||
if (usersFromLists.ContainsKey(u))
|
if (usersFromLists.ContainsKey(username))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (!allUsersAndLists.Users.TryGetValue(u, out long userId))
|
usersFromLists[username] = userId;
|
||||||
continue;
|
|
||||||
|
|
||||||
usersFromLists[u] = userId;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user