forked from sim0n00ps/OF-DL
Compare commits
4 Commits
a7906f84f9
...
a4f963e8f6
| Author | SHA1 | Date | |
|---|---|---|---|
| a4f963e8f6 | |||
| cdfe83b8dd | |||
| 6a9c48bf3e | |||
| 8dc80c350c |
@ -2,7 +2,12 @@ using OF_DL.Models.Downloads;
|
|||||||
|
|
||||||
namespace OF_DL.CLI;
|
namespace OF_DL.CLI;
|
||||||
|
|
||||||
internal class CajetanDownloadEventHandler : IDownloadEventHandler
|
public interface ICajetanDownloadEventHandler : IDownloadEventHandler
|
||||||
|
{
|
||||||
|
void OnMessage(string message, string color);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CajetanDownloadEventHandler : ICajetanDownloadEventHandler
|
||||||
{
|
{
|
||||||
private readonly SpectreDownloadEventHandler _eventHandler = new();
|
private readonly SpectreDownloadEventHandler _eventHandler = new();
|
||||||
|
|
||||||
@ -15,6 +20,9 @@ internal class CajetanDownloadEventHandler : IDownloadEventHandler
|
|||||||
public void OnMessage(string message)
|
public void OnMessage(string message)
|
||||||
=> _eventHandler.OnMessage(message);
|
=> _eventHandler.OnMessage(message);
|
||||||
|
|
||||||
|
public void OnMessage(string message, string color)
|
||||||
|
=> AnsiConsole.Markup($"[{color.ToLowerInvariant()}]{Markup.Escape(message)}\n[/]");
|
||||||
|
|
||||||
public void OnNoContentFound(string contentType)
|
public void OnNoContentFound(string contentType)
|
||||||
=> _eventHandler.OnNoContentFound(contentType);
|
=> _eventHandler.OnNoContentFound(contentType);
|
||||||
|
|
||||||
|
|||||||
19
Cajetan.OF-DL/Models/Dtos/Messages/ChatsDto.cs
Normal file
19
Cajetan.OF-DL/Models/Dtos/Messages/ChatsDto.cs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
namespace OF_DL.Models.Dtos.Messages;
|
||||||
|
|
||||||
|
public class ChatsDto
|
||||||
|
{
|
||||||
|
[JsonProperty("list")] public List<ChatItemDto> List { get; set; } = [];
|
||||||
|
[JsonProperty("hasMore")] public bool HasMore { get; set; }
|
||||||
|
[JsonProperty("nextOffset")] public int NextOffset { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ChatItemDto
|
||||||
|
{
|
||||||
|
[JsonProperty("withUser")] public ChatUserDto WithUser { get; set; } = new();
|
||||||
|
[JsonProperty("unreadMessagesCount")] public int UnreadMessagesCount { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ChatUserDto
|
||||||
|
{
|
||||||
|
[JsonProperty("id")] public long Id { get; set; }
|
||||||
|
}
|
||||||
@ -3,7 +3,10 @@ using System.Reflection;
|
|||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
AnsiConsole.Write(new FigletText("Welcome to OF-DL").Color(Color.Red));
|
AnsiConsole.Write(new FigletText("Welcome to OF-DL").Color(Color.Red));
|
||||||
|
await RunAsync(args);
|
||||||
|
|
||||||
|
static async Task RunAsync(string[] args)
|
||||||
|
{
|
||||||
ServiceCollection services = await ConfigureServices(args);
|
ServiceCollection services = await ConfigureServices(args);
|
||||||
ServiceProvider serviceProvider = services.BuildServiceProvider();
|
ServiceProvider serviceProvider = services.BuildServiceProvider();
|
||||||
|
|
||||||
@ -11,6 +14,7 @@ ExitIfOtherProcess(serviceProvider);
|
|||||||
|
|
||||||
Worker worker = serviceProvider.GetRequiredService<Worker>();
|
Worker worker = serviceProvider.GetRequiredService<Worker>();
|
||||||
await worker.RunAsync();
|
await worker.RunAsync();
|
||||||
|
}
|
||||||
|
|
||||||
static async Task<ServiceCollection> ConfigureServices(string[] args)
|
static async Task<ServiceCollection> ConfigureServices(string[] args)
|
||||||
{
|
{
|
||||||
@ -62,12 +66,24 @@ static async Task<ServiceCollection> ConfigureServices(string[] args)
|
|||||||
services.AddSingleton(cajetanConfig);
|
services.AddSingleton(cajetanConfig);
|
||||||
|
|
||||||
services.AddSingleton<IAuthService, AuthService>();
|
services.AddSingleton<IAuthService, AuthService>();
|
||||||
services.AddSingleton<IApiService, ApiService>();
|
|
||||||
services.AddSingleton<IDbService, DbService>();
|
|
||||||
services.AddSingleton<IDownloadService, DownloadService>();
|
|
||||||
services.AddSingleton<IFileNameService, FileNameService>();
|
|
||||||
services.AddSingleton<IStartupService, StartupService>();
|
services.AddSingleton<IStartupService, StartupService>();
|
||||||
services.AddSingleton<IDownloadOrchestrationService, DownloadOrchestrationService>();
|
services.AddSingleton<IFileNameService, FileNameService>();
|
||||||
|
|
||||||
|
services.AddSingleton<ICajetanDownloadService, CajetanDownloadService>();
|
||||||
|
services.AddSingleton<IDownloadService>(sp => sp.GetRequiredService<ICajetanDownloadService>());
|
||||||
|
|
||||||
|
services.AddSingleton<ICajetanApiService, CajetanApiService>();
|
||||||
|
services.AddSingleton<IApiService>(sp => sp.GetRequiredService<ICajetanApiService>());
|
||||||
|
|
||||||
|
services.AddSingleton<ICajetanDbService, CajetanDbService>();
|
||||||
|
services.AddSingleton<IDbService>(sp => sp.GetRequiredService<ICajetanDbService>());
|
||||||
|
|
||||||
|
|
||||||
|
services.AddSingleton<ICajetanDownloadOrchestrationService, CajetanDownloadOrchestrationService>();
|
||||||
|
services.AddSingleton<IDownloadOrchestrationService>(sp => sp.GetRequiredService<ICajetanDownloadOrchestrationService>());
|
||||||
|
|
||||||
|
services.AddSingleton<ICajetanDownloadEventHandler, CajetanDownloadEventHandler>();
|
||||||
|
services.AddSingleton<IDownloadEventHandler>(sp => sp.GetRequiredService<ICajetanDownloadEventHandler>());
|
||||||
|
|
||||||
services.AddSingleton<Worker>();
|
services.AddSingleton<Worker>();
|
||||||
|
|
||||||
|
|||||||
@ -2,11 +2,10 @@ global using Serilog;
|
|||||||
global using Spectre.Console;
|
global using Spectre.Console;
|
||||||
global using OF_DL;
|
global using OF_DL;
|
||||||
global using OF_DL.CLI;
|
global using OF_DL.CLI;
|
||||||
global using OF_DL.Crypto;
|
|
||||||
global using OF_DL.Enumerations;
|
global using OF_DL.Enumerations;
|
||||||
global using OF_DL.Exceptions;
|
global using OF_DL.Exceptions;
|
||||||
global using OF_DL.Helpers;
|
global using OF_DL.Helpers;
|
||||||
global using OF_DL.Models;
|
global using OF_DL.Models;
|
||||||
global using OF_DL.Models.Config;
|
global using OF_DL.Models.Config;
|
||||||
global using OF_DL.Services;
|
global using OF_DL.Services;
|
||||||
global using OF_DL.Utils;
|
global using Newtonsoft.Json;
|
||||||
|
|||||||
223
Cajetan.OF-DL/Services/CajetanApiService.cs
Normal file
223
Cajetan.OF-DL/Services/CajetanApiService.cs
Normal file
@ -0,0 +1,223 @@
|
|||||||
|
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)
|
||||||
|
: ApiService(authService, configService, dbService), ICajetanApiService
|
||||||
|
{
|
||||||
|
private readonly ICajetanDownloadEventHandler _eventHandler = eventHandler;
|
||||||
|
|
||||||
|
public new async Task<UserEntities.User?> GetUserInfo(string endpoint)
|
||||||
|
{
|
||||||
|
UserEntities.UserInfo? userInfo = await GetDetailedUserInfo(endpoint);
|
||||||
|
|
||||||
|
if (userInfo is not null && !endpoint.EndsWith("/me"))
|
||||||
|
await dbService.UpdateUserInfoAsync(userInfo);
|
||||||
|
|
||||||
|
return userInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<UserEntities.UserInfo?> GetDetailedUserInfo(string endpoint)
|
||||||
|
{
|
||||||
|
Log.Debug($"Calling GetDetailedUserInfo: {endpoint}");
|
||||||
|
|
||||||
|
if (!HasSignedRequestAuth())
|
||||||
|
return null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
UserEntities.UserInfo userInfo = new();
|
||||||
|
Dictionary<string, string> getParams = new()
|
||||||
|
{
|
||||||
|
{ "limit", Constants.ApiPageSize.ToString() }, { "order", "publish_date_asc" }
|
||||||
|
};
|
||||||
|
|
||||||
|
HttpClient client = new();
|
||||||
|
HttpRequestMessage request = await BuildHttpRequestMessage(getParams, endpoint);
|
||||||
|
|
||||||
|
using HttpResponseMessage response = await client.SendAsync(request);
|
||||||
|
|
||||||
|
if (!response.IsSuccessStatusCode)
|
||||||
|
return userInfo;
|
||||||
|
|
||||||
|
response.EnsureSuccessStatusCode();
|
||||||
|
string body = await response.Content.ReadAsStringAsync();
|
||||||
|
UserDtos.UserDto? userDto = JsonConvert.DeserializeObject<UserDtos.UserDto>(body, s_mJsonSerializerSettings);
|
||||||
|
userInfo = FromDto(userDto);
|
||||||
|
return userInfo;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
ExceptionLoggerHelper.LogException(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
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()
|
||||||
|
{
|
||||||
|
MessageDtos.ChatsDto unreadChats = await GetChatsAsync("", onlyUnread: true);
|
||||||
|
HashSet<long> userWithUnread = [];
|
||||||
|
|
||||||
|
foreach (MessageDtos.ChatItemDto chatItem in unreadChats.List)
|
||||||
|
{
|
||||||
|
if (chatItem?.WithUser?.Id is null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (chatItem.UnreadMessagesCount <= 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
userWithUnread.Add(chatItem.WithUser.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return userWithUnread;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task MarkAsUnreadAsync(string endpoint)
|
||||||
|
{
|
||||||
|
Log.Debug($"Calling MarkAsUnread - {endpoint}");
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var result = new { success = false };
|
||||||
|
|
||||||
|
string? body = await BuildHeaderAndExecuteRequests([], endpoint, GetHttpClient(), HttpMethod.Delete);
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(body))
|
||||||
|
result = JsonConvert.DeserializeAnonymousType(body, result);
|
||||||
|
|
||||||
|
if (result?.success != true)
|
||||||
|
_eventHandler.OnMessage($"Failed to mark chat as unread! Endpoint: {endpoint}", "yellow");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
ExceptionLoggerHelper.LogException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<MessageDtos.ChatsDto> GetChatsAsync(string endpoint, bool onlyUnread)
|
||||||
|
{
|
||||||
|
Log.Debug($"Calling GetChats - {endpoint}");
|
||||||
|
|
||||||
|
MessageDtos.ChatsDto allChats = new();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
int limit = 60;
|
||||||
|
Dictionary<string, string> getParams = new()
|
||||||
|
{
|
||||||
|
{ "limit", $"{limit}" },
|
||||||
|
{ "offset", "0" },
|
||||||
|
{ "skip_users", "all" },
|
||||||
|
{ "order", "recent" }
|
||||||
|
};
|
||||||
|
|
||||||
|
if (onlyUnread)
|
||||||
|
getParams["filter"] = "unread";
|
||||||
|
|
||||||
|
string? body = await BuildHeaderAndExecuteRequests(getParams, endpoint, GetHttpClient());
|
||||||
|
MessageDtos.ChatsDto? chats = DeserializeJson<MessageDtos.ChatsDto>(body, s_mJsonSerializerSettings);
|
||||||
|
|
||||||
|
if (chats is null)
|
||||||
|
return allChats;
|
||||||
|
|
||||||
|
if (chats.HasMore)
|
||||||
|
{
|
||||||
|
getParams["offset"] = $"{chats.NextOffset}";
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
string? loopbody = await BuildHeaderAndExecuteRequests(getParams, endpoint, GetHttpClient());
|
||||||
|
MessageDtos.ChatsDto? newChats = DeserializeJson<MessageDtos.ChatsDto>(loopbody, s_mJsonSerializerSettings);
|
||||||
|
|
||||||
|
if (newChats is null)
|
||||||
|
break;
|
||||||
|
|
||||||
|
allChats.List.AddRange(newChats.List);
|
||||||
|
|
||||||
|
if (!newChats.HasMore)
|
||||||
|
break;
|
||||||
|
|
||||||
|
getParams["offset"] = $"{newChats.NextOffset}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
ExceptionLoggerHelper.LogException(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return allChats;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static UserEntities.UserInfo FromDto(UserDtos.UserDto? userDto)
|
||||||
|
{
|
||||||
|
if (userDto is null)
|
||||||
|
return new();
|
||||||
|
|
||||||
|
return new()
|
||||||
|
{
|
||||||
|
Id = userDto.Id,
|
||||||
|
|
||||||
|
Avatar = userDto.Avatar,
|
||||||
|
Header = userDto.Header,
|
||||||
|
Name = userDto.Name,
|
||||||
|
Username = userDto.Username,
|
||||||
|
|
||||||
|
SubscribePrice = userDto.SubscribePrice,
|
||||||
|
CurrentSubscribePrice = userDto.CurrentSubscribePrice,
|
||||||
|
IsPaywallRequired = userDto.IsPaywallRequired,
|
||||||
|
IsRestricted = userDto.IsRestricted,
|
||||||
|
SubscribedBy = userDto.SubscribedBy,
|
||||||
|
SubscribedByExpire = userDto.SubscribedByExpire,
|
||||||
|
SubscribedByExpireDate = userDto.SubscribedByExpireDate,
|
||||||
|
SubscribedByAutoprolong = userDto.SubscribedByAutoprolong,
|
||||||
|
SubscribedIsExpiredNow = userDto.SubscribedIsExpiredNow,
|
||||||
|
SubscribedOn = userDto.SubscribedOn,
|
||||||
|
SubscribedOnExpiredNow = userDto.SubscribedOnExpiredNow,
|
||||||
|
SubscribedOnDuration = userDto.SubscribedOnDuration,
|
||||||
|
About = userDto.About,
|
||||||
|
PostsCount = userDto.PostsCount,
|
||||||
|
ArchivedPostsCount = userDto.ArchivedPostsCount,
|
||||||
|
PrivateArchivedPostsCount = userDto.PrivateArchivedPostsCount,
|
||||||
|
PhotosCount = userDto.PhotosCount,
|
||||||
|
VideosCount = userDto.VideosCount,
|
||||||
|
AudiosCount = userDto.AudiosCount,
|
||||||
|
MediasCount = userDto.MediasCount,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
107
Cajetan.OF-DL/Services/CajetanDbService.cs
Normal file
107
Cajetan.OF-DL/Services/CajetanDbService.cs
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
using Microsoft.Data.Sqlite;
|
||||||
|
using OF_DL.Models.Entities.Users;
|
||||||
|
|
||||||
|
namespace OF_DL.Services;
|
||||||
|
|
||||||
|
public class CajetanDbService(IConfigService configService)
|
||||||
|
: DbService(configService), ICajetanDbService
|
||||||
|
{
|
||||||
|
public async Task InitializeUserInfoTablesAsync()
|
||||||
|
{
|
||||||
|
await using SqliteConnection connection = new($"Data Source={Directory.GetCurrentDirectory()}/users.db");
|
||||||
|
|
||||||
|
|
||||||
|
using (SqliteCommand cmdInfo = new("CREATE TABLE IF NOT EXISTS user_info (user_id INTEGER NOT NULL, name VARCHAR NOT NULL, about VARCHAR NULL, expires_on TIMESTAMP NULL, photo_count INT NOT NULL, video_count INT NOT NULL, PRIMARY KEY(user_id));", connection))
|
||||||
|
{
|
||||||
|
await cmdInfo.ExecuteNonQueryAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
using (SqliteCommand cmdInfo = new("CREATE TABLE IF NOT EXISTS user_info_blob (user_id INTEGER NOT NULL, name VARCHAR NOT NULL, blob TEXT NULL, PRIMARY KEY(user_id));", connection))
|
||||||
|
{
|
||||||
|
await cmdInfo.ExecuteNonQueryAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Dictionary<string, long>> GetUsersAsync()
|
||||||
|
{
|
||||||
|
await using SqliteConnection connection = new($"Data Source={Directory.GetCurrentDirectory()}/users.db");
|
||||||
|
|
||||||
|
using SqliteCommand cmd = new("SELECT user_id, username FROM users", connection);
|
||||||
|
using SqliteDataReader reader = cmd.ExecuteReader();
|
||||||
|
|
||||||
|
Dictionary<string, long> result = new(StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
while (reader.Read())
|
||||||
|
{
|
||||||
|
long userId = reader.GetInt64(0);
|
||||||
|
string username = reader.GetString(1);
|
||||||
|
|
||||||
|
result[username] = userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task UpdateUserInfoAsync(UserInfo? userInfo)
|
||||||
|
{
|
||||||
|
if (userInfo?.Id is null || userInfo?.Username is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
await using SqliteConnection connection = new($"Data Source={Directory.GetCurrentDirectory()}/users.db");
|
||||||
|
|
||||||
|
Log.Debug("Database data source: " + connection.DataSource);
|
||||||
|
|
||||||
|
await UpdateAsync();
|
||||||
|
await UpdateBlobAsync();
|
||||||
|
|
||||||
|
async Task UpdateAsync()
|
||||||
|
{
|
||||||
|
using SqliteCommand cmdInfo = new(
|
||||||
|
"INSERT OR REPLACE INTO user_info (user_id, name, about, expires_on, photo_count, video_count) " +
|
||||||
|
"VALUES (@userId, @name, @about, @expiresOn, @photoCount, @videoCount);",
|
||||||
|
connection
|
||||||
|
);
|
||||||
|
|
||||||
|
cmdInfo.Parameters.AddWithValue("@userId", userInfo.Id);
|
||||||
|
cmdInfo.Parameters.AddWithValue("@name", userInfo.Name ?? userInfo.Username);
|
||||||
|
cmdInfo.Parameters.AddWithValue("@about", userInfo.About);
|
||||||
|
cmdInfo.Parameters.AddWithValue("@expiresOn", userInfo.SubscribedByExpireDate);
|
||||||
|
cmdInfo.Parameters.AddWithValue("@photoCount", userInfo.PhotosCount ?? 0);
|
||||||
|
cmdInfo.Parameters.AddWithValue("@videoCount", userInfo.VideosCount ?? 0);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await cmdInfo.ExecuteNonQueryAsync();
|
||||||
|
Log.Debug("Inserted or updated creator info: {Username:l}", userInfo.Username);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log.Warning(ex, "Failed to update User Info for: {Username:l}", userInfo.Username);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async Task UpdateBlobAsync()
|
||||||
|
{
|
||||||
|
using SqliteCommand cmdInfo = new(
|
||||||
|
"INSERT OR REPLACE INTO user_info_blob (user_id, name, blob) " +
|
||||||
|
"VALUES (@userId, @name, @blob);",
|
||||||
|
connection
|
||||||
|
);
|
||||||
|
|
||||||
|
cmdInfo.Parameters.AddWithValue("@userId", userInfo.Id);
|
||||||
|
cmdInfo.Parameters.AddWithValue("@name", userInfo.Name ?? userInfo.Username);
|
||||||
|
cmdInfo.Parameters.AddWithValue("@blob", Newtonsoft.Json.JsonConvert.SerializeObject(userInfo));
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await cmdInfo.ExecuteNonQueryAsync();
|
||||||
|
Log.Debug("Inserted or updated creator blob: {Username:l}", userInfo.Username);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log.Warning(ex, "Failed to update User Info Blob for: {Username:l}", userInfo.Username);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
namespace OF_DL.Services;
|
||||||
|
|
||||||
|
public class CajetanDownloadOrchestrationService(ICajetanApiService apiService, IConfigService configService, ICajetanDownloadService downloadService, ICajetanDbService dbService)
|
||||||
|
: DownloadOrchestrationService(apiService, configService, downloadService, dbService), ICajetanDownloadOrchestrationService
|
||||||
|
{
|
||||||
|
}
|
||||||
6
Cajetan.OF-DL/Services/CajetanDownloadService.cs
Normal file
6
Cajetan.OF-DL/Services/CajetanDownloadService.cs
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
namespace OF_DL.Services;
|
||||||
|
|
||||||
|
public class CajetanDownloadService(IAuthService authService, IConfigService configService, ICajetanDbService dbService, IFileNameService fileNameService, ICajetanApiService apiService)
|
||||||
|
: DownloadService(authService, configService, dbService, fileNameService, apiService), ICajetanDownloadService
|
||||||
|
{
|
||||||
|
}
|
||||||
11
Cajetan.OF-DL/Services/ICajetanApiService.cs
Normal file
11
Cajetan.OF-DL/Services/ICajetanApiService.cs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
using UserEntities = OF_DL.Models.Entities.Users;
|
||||||
|
|
||||||
|
namespace OF_DL.Services;
|
||||||
|
|
||||||
|
public interface ICajetanApiService : IApiService
|
||||||
|
{
|
||||||
|
Task<UserEntities.UserInfo?> GetDetailedUserInfo(string endpoint);
|
||||||
|
|
||||||
|
Task<HashSet<long>> GetUsersWithUnreadMessagesAsync();
|
||||||
|
Task MarkAsUnreadAsync(string endpoint);
|
||||||
|
}
|
||||||
12
Cajetan.OF-DL/Services/ICajetanDbService.cs
Normal file
12
Cajetan.OF-DL/Services/ICajetanDbService.cs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
|
||||||
|
using OF_DL.Models.Entities.Users;
|
||||||
|
|
||||||
|
namespace OF_DL.Services;
|
||||||
|
|
||||||
|
public interface ICajetanDbService : IDbService
|
||||||
|
{
|
||||||
|
Task InitializeUserInfoTablesAsync();
|
||||||
|
|
||||||
|
Task<Dictionary<string, long>> GetUsersAsync();
|
||||||
|
Task UpdateUserInfoAsync(UserInfo? userInfo);
|
||||||
|
}
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
namespace OF_DL.Services;
|
||||||
|
|
||||||
|
public interface ICajetanDownloadOrchestrationService : IDownloadOrchestrationService
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
6
Cajetan.OF-DL/Services/ICajetanDownloadService.cs
Normal file
6
Cajetan.OF-DL/Services/ICajetanDownloadService.cs
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
namespace OF_DL.Services;
|
||||||
|
|
||||||
|
public interface ICajetanDownloadService : IDownloadService
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
@ -10,8 +10,8 @@ internal class Worker(IServiceProvider serviceProvider)
|
|||||||
private readonly IAuthService _authService = serviceProvider.GetRequiredService<IAuthService>();
|
private readonly IAuthService _authService = serviceProvider.GetRequiredService<IAuthService>();
|
||||||
private readonly IStartupService _startupService = serviceProvider.GetRequiredService<IStartupService>();
|
private readonly IStartupService _startupService = serviceProvider.GetRequiredService<IStartupService>();
|
||||||
private readonly IDownloadOrchestrationService _orchestrationService = serviceProvider.GetRequiredService<IDownloadOrchestrationService>();
|
private readonly IDownloadOrchestrationService _orchestrationService = serviceProvider.GetRequiredService<IDownloadOrchestrationService>();
|
||||||
private readonly IDbService _dbService = serviceProvider.GetRequiredService<IDbService>();
|
private readonly ICajetanDbService _dbService = serviceProvider.GetRequiredService<ICajetanDbService>();
|
||||||
private readonly IApiService _apiService = serviceProvider.GetRequiredService<IApiService>();
|
private readonly ICajetanApiService _apiService = serviceProvider.GetRequiredService<ICajetanApiService>();
|
||||||
private readonly ExitHelper _exitHelper = serviceProvider.GetRequiredService<ExitHelper>();
|
private readonly ExitHelper _exitHelper = serviceProvider.GetRequiredService<ExitHelper>();
|
||||||
|
|
||||||
private readonly CajetanConfig _cajetanConfig = serviceProvider.GetRequiredService<CajetanConfig>();
|
private readonly CajetanConfig _cajetanConfig = serviceProvider.GetRequiredService<CajetanConfig>();
|
||||||
@ -288,6 +288,7 @@ internal class Worker(IServiceProvider serviceProvider)
|
|||||||
AnsiConsole.WriteLine();
|
AnsiConsole.WriteLine();
|
||||||
|
|
||||||
await _dbService.CreateUsersDb(result.Users);
|
await _dbService.CreateUsersDb(result.Users);
|
||||||
|
await _dbService.InitializeUserInfoTablesAsync();
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
|
|||||||
36
OF DL.Core/Models/Entities/Users/UserInfo.cs
Normal file
36
OF DL.Core/Models/Entities/Users/UserInfo.cs
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
namespace OF_DL.Models.Entities.Users;
|
||||||
|
|
||||||
|
public class UserInfo : User
|
||||||
|
{
|
||||||
|
public long? Id { get; set; }
|
||||||
|
|
||||||
|
public string? SubscribePrice { get; set; }
|
||||||
|
public string? CurrentSubscribePrice { get; set; }
|
||||||
|
|
||||||
|
public bool? IsPaywallRequired { get; set; }
|
||||||
|
public bool? IsActive { get; set; }
|
||||||
|
public bool? IsRestricted { get; set; }
|
||||||
|
|
||||||
|
public bool? SubscribedBy { get; set; }
|
||||||
|
public bool? SubscribedByExpire { get; set; }
|
||||||
|
public DateTimeOffset? SubscribedByExpireDate { get; set; }
|
||||||
|
public bool? SubscribedByAutoprolong { get; set; }
|
||||||
|
public bool? IsPendingAutoprolong { get; set; }
|
||||||
|
|
||||||
|
public bool? SubscribedIsExpiredNow { get; set; }
|
||||||
|
|
||||||
|
public bool? SubscribedOn { get; set; }
|
||||||
|
public bool? SubscribedOnExpiredNow { get; set; }
|
||||||
|
public string? SubscribedOnDuration { get; set; }
|
||||||
|
|
||||||
|
public string? About { get; set; }
|
||||||
|
|
||||||
|
public int? PostsCount { get; set; }
|
||||||
|
public int? ArchivedPostsCount { get; set; }
|
||||||
|
public int? PrivateArchivedPostsCount { get; set; }
|
||||||
|
|
||||||
|
public int? PhotosCount { get; set; }
|
||||||
|
public int? VideosCount { get; set; }
|
||||||
|
public int? AudiosCount { get; set; }
|
||||||
|
public int? MediasCount { get; set; }
|
||||||
|
}
|
||||||
@ -23,4 +23,8 @@
|
|||||||
<PackageReference Include="xFFmpeg.NET" Version="7.2.0"/>
|
<PackageReference Include="xFFmpeg.NET" Version="7.2.0"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<InternalsVisibleTo Include="Cajetan.OF-DL" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@ -43,7 +43,7 @@ public class ApiService(IAuthService authService, IConfigService configService,
|
|||||||
{
|
{
|
||||||
private const int MaxAttempts = 30;
|
private const int MaxAttempts = 30;
|
||||||
private const int DelayBetweenAttempts = 3000;
|
private const int DelayBetweenAttempts = 3000;
|
||||||
private static readonly JsonSerializerSettings s_mJsonSerializerSettings;
|
protected static readonly JsonSerializerSettings s_mJsonSerializerSettings;
|
||||||
private static DateTime? s_cachedDynamicRulesExpiration;
|
private static DateTime? s_cachedDynamicRulesExpiration;
|
||||||
private static DynamicRules? s_cachedDynamicRules;
|
private static DynamicRules? s_cachedDynamicRules;
|
||||||
|
|
||||||
@ -147,7 +147,7 @@ public class ApiService(IAuthService authService, IConfigService configService,
|
|||||||
return headers;
|
return headers;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool HasSignedRequestAuth()
|
protected bool HasSignedRequestAuth()
|
||||||
{
|
{
|
||||||
Auth? currentAuth = authService.CurrentAuth;
|
Auth? currentAuth = authService.CurrentAuth;
|
||||||
return currentAuth is { UserId: not null, Cookie: not null, UserAgent: not null, XBc: not null };
|
return currentAuth is { UserId: not null, Cookie: not null, UserAgent: not null, XBc: not null };
|
||||||
@ -2772,12 +2772,12 @@ public class ApiService(IAuthService authService, IConfigService configService,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private async Task<string?> BuildHeaderAndExecuteRequests(Dictionary<string, string> getParams, string endpoint,
|
protected async Task<string?> BuildHeaderAndExecuteRequests(Dictionary<string, string> getParams, string endpoint,
|
||||||
HttpClient client)
|
HttpClient client, HttpMethod? method = null)
|
||||||
{
|
{
|
||||||
Log.Debug("Calling BuildHeaderAndExecuteRequests");
|
Log.Debug("Calling BuildHeaderAndExecuteRequests");
|
||||||
|
|
||||||
HttpRequestMessage request = await BuildHttpRequestMessage(getParams, endpoint);
|
HttpRequestMessage request = await BuildHttpRequestMessage(getParams, endpoint, method);
|
||||||
using HttpResponseMessage response = await client.SendAsync(request);
|
using HttpResponseMessage response = await client.SendAsync(request);
|
||||||
response.EnsureSuccessStatusCode();
|
response.EnsureSuccessStatusCode();
|
||||||
string body = await response.Content.ReadAsStringAsync();
|
string body = await response.Content.ReadAsStringAsync();
|
||||||
@ -2788,16 +2788,19 @@ public class ApiService(IAuthService authService, IConfigService configService,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private Task<HttpRequestMessage> BuildHttpRequestMessage(Dictionary<string, string> getParams,
|
protected Task<HttpRequestMessage> BuildHttpRequestMessage(Dictionary<string, string> getParams,
|
||||||
string endpoint)
|
string endpoint, HttpMethod? method = null)
|
||||||
{
|
{
|
||||||
Log.Debug("Calling BuildHttpRequestMessage");
|
Log.Debug("Calling BuildHttpRequestMessage");
|
||||||
|
|
||||||
string queryParams = "?" + string.Join("&", getParams.Select(kvp => $"{kvp.Key}={kvp.Value}"));
|
string queryParams = "";
|
||||||
|
|
||||||
|
if (getParams.Count != 0)
|
||||||
|
queryParams = "?" + string.Join("&", getParams.Select(kvp => $"{kvp.Key}={kvp.Value}"));
|
||||||
|
|
||||||
Dictionary<string, string> headers = GetDynamicHeaders($"/api2/v2{endpoint}", queryParams);
|
Dictionary<string, string> headers = GetDynamicHeaders($"/api2/v2{endpoint}", queryParams);
|
||||||
|
|
||||||
HttpRequestMessage request = new(HttpMethod.Get, $"{Constants.ApiUrl}{endpoint}{queryParams}");
|
HttpRequestMessage request = new(method ?? HttpMethod.Get, $"{Constants.ApiUrl}{endpoint}{queryParams}");
|
||||||
|
|
||||||
Log.Debug($"Full request URL: {Constants.ApiUrl}{endpoint}{queryParams}");
|
Log.Debug($"Full request URL: {Constants.ApiUrl}{endpoint}{queryParams}");
|
||||||
|
|
||||||
@ -2821,7 +2824,7 @@ public class ApiService(IAuthService authService, IConfigService configService,
|
|||||||
private static bool IsStringOnlyDigits(string input) => input.All(char.IsDigit);
|
private static bool IsStringOnlyDigits(string input) => input.All(char.IsDigit);
|
||||||
|
|
||||||
|
|
||||||
private HttpClient GetHttpClient()
|
protected HttpClient GetHttpClient()
|
||||||
{
|
{
|
||||||
HttpClient client = new();
|
HttpClient client = new();
|
||||||
if (configService.CurrentConfig.Timeout is > 0)
|
if (configService.CurrentConfig.Timeout is > 0)
|
||||||
@ -2832,7 +2835,7 @@ public class ApiService(IAuthService authService, IConfigService configService,
|
|||||||
return client;
|
return client;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static T? DeserializeJson<T>(string? body, JsonSerializerSettings? settings = null)
|
protected static T? DeserializeJson<T>(string? body, JsonSerializerSettings? settings = null)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(body))
|
if (string.IsNullOrWhiteSpace(body))
|
||||||
{
|
{
|
||||||
|
|||||||
@ -51,4 +51,8 @@
|
|||||||
</None>
|
</None>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<InternalsVisibleTo Include="Cajetan.OF-DL" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user