diff --git a/.idea/.idea.Linkding.Sync/.idea/.name b/.idea/.idea.Linkding.Sync/.idea/.name new file mode 100644 index 0000000..bbf3e88 --- /dev/null +++ b/.idea/.idea.Linkding.Sync/.idea/.name @@ -0,0 +1 @@ +Linkding.Sync \ No newline at end of file diff --git a/.idea/.idea.Linkding.Sync/.idea/GitLink.xml b/.idea/.idea.Linkding.Sync/.idea/GitLink.xml new file mode 100644 index 0000000..009597c --- /dev/null +++ b/.idea/.idea.Linkding.Sync/.idea/GitLink.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/README.md b/README.md index 1cb70a3..06db8f1 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ Environment variables for the wallabag worker. | Variable | Value | Description | Attention | |------------------------|-----------|--------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------| -| Worker__Intervall | int (>=0) | This value sets the execution schedule in minutes. 1 = every minute, 10 = every 10 minutes (default value 0) | 0 = runs only one time. The container will be stopped after the execution. This method is the preferred way to run the container with a scheduler (e.g. cron) | +| Worker__Interval | int (>=0) | This value sets the execution schedule in minutes. 1 = every minute, 10 = every 10 minutes (default value 0) | 0 = runs only one time. The container will be stopped after the execution. This method is the preferred way to run the container with a scheduler (e.g. cron) | | Worker__SyncTag | text | The linkding tag to create the bookmarks in Wallabag. (default value 'readlater') | | | Linkding__Url | text | URL to the linkding instance | | | Linkding__Key | text | The linkding application key | [Instructions](https://github.com/sissbruecker/linkding/blob/master/docs/API.md) | @@ -27,12 +27,45 @@ Environment variables for the wallabag worker. ### LinkdingUpdater Environment variables for the linkding worker. -| Variable | Value | Description | Attention | -|------------------------|-----------|--------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------| -| Worker__Intervall | int (>=0) | This value sets the execution schedule in minutes. 1 = every minute, 10 = every 10 minutes | 0 = runs only one time. The container will be stopped after the execution. This method is the preferred way to run the container with a scheduler (e.g. cron) | -| Worker__SyncTag | text | The linkding tag to create the bookmarks in Wallabag. | | -| Linkding__Url | text | URL to the linkding instance | | -| Linkding__Key | text | The linkding application key | [Instructions](https://github.com/sissbruecker/linkding/blob/master/docs/API.md) | +| Variable | Value | Description | Attention | +|-------------------------------|-----------|--------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------| +| Worker__Interval | int (>=0) | This value sets the execution schedule in minutes. 1 = every minute, 10 = every 10 minutes | 0 = runs only one time. The container will be stopped after the execution. This method is the preferred way to run the container with a scheduler (e.g. cron) | +| Worker__Worker__TagNameLength | int | The max tag name length. Default is 64 characters | | +| Worker__Tasks__X | text | The tasks which should be executed. X is the array index for the tasks entry | currently supported tasks: AddPopularSitesAsTag,AddYearToBookmark. | +| Linkding__Url | text | URL to the linkding instance | | +| Linkding__Key | text | The linkding application key | [Instructions](https://github.com/sissbruecker/linkding/blob/master/docs/API.md) | + +#### Tasks +Tasks define the logic that directly make changes to the bookmarks. +Currently available tasks: + +| Name | Description | +|-----------------------|------------------------------------------------------------------------------------------------------------------------------------| +| AddYearToBookmark | If the bookmark does not have the year of the creation date as a tag, it will be added. | +| AddPopularSitesAsTag | This is the task for dynamic creation of tags. For this task to work, the rules must be passed to the container as configuration. | + +The tasks are defined as environment variables, if no tasks are defined, the container will be executed but no changes will be made. +The tasks are passed as follows: +```yaml +version: '3.9' + +services: + linkdingupdater: + image: ghcr.io/spaytac/linkding-updater:latest + volumes: + - ./config.yml:/app/data/config.yml + # env_file: + # - .env + environment: + - Worker__Interval=0 + - Worker__TagNameLength=64 + - Worker__Tasks__0=AddPopularSitesAsTag + - Worker__Tasks__1=AddYearToBookmark + - Linkding__Url=https:// + - Linkding__Key= + + +``` ## Configuration The following explains the configuration options. The Configuration file must be mapped to **/app/data/config.yml** diff --git a/examples/linkding/config.yml b/examples/linkding/config.yml index 1650e79..4ccaa8c 100644 --- a/examples/linkding/config.yml +++ b/examples/linkding/config.yml @@ -32,8 +32,8 @@ taggingRule: pattern: https://[[a-zA-Z0-9]+\.]?(xbox)\.com(?:/.*)? replace: $1 - name: github - pattern: https://([a-zA-Z0-9]+)?[\.]?(github)\.com[/]?([a-zA-Z0-9\-\+_]+)(?:/)?([a-zA-Z0-9\-\+_]+)?(?:/.*)? - replace: $1,$2,$3,$4 + pattern: https://([ a-zA-Z0-9 ]+)?[ \. ]?(github)\.com[ / ]?([ a-zA-Z0-9\-\+_ ]+)(?:/)?([ a-zA-Z0-9\-\+_ ]+)?(?:/.*)? + replace: $2,$3,$4 - name: github.io - pattern: https://([a-zA-Z0-9]+)\.(github)\.io[/]?([a-zA-Z0-9\-\+_]+)(?:/)?([a-zA-Z0-9\-\+_]+)?(?:/.*)? + pattern: https://([ a-zA-Z0-9 ]+)\.(github)\.io[ / ]?([ a-zA-Z0-9\-\+_ ]+)(?:/)?([ a-zA-Z0-9\-\+_ ]+)?(?:/.*)? replace: $1,$2,$3 \ No newline at end of file diff --git a/examples/linkding/docker-compose.yml b/examples/linkding/docker-compose.yml index 9c07970..3bb079d 100644 --- a/examples/linkding/docker-compose.yml +++ b/examples/linkding/docker-compose.yml @@ -8,6 +8,9 @@ services: # env_file: # - .env environment: - - Worker__Intervall=0 + - Worker__Interval=0 + - Worker__TagNameLength=64 + - Worker__Tasks__0=AddPopularSitesAsTag + - Worker__Tasks__1=AddYearToBookmark - Linkding__Url=https:// - Linkding__Key= diff --git a/examples/wallabag/docker-compose.yml b/examples/wallabag/docker-compose.yml index e32f13f..01fcc88 100644 --- a/examples/wallabag/docker-compose.yml +++ b/examples/wallabag/docker-compose.yml @@ -8,7 +8,7 @@ services: # env_file: # - .env environment: - - Worker__Intervall=0 + - Worker__Interval=0 - Worker__SyncTag= - Linkding__Url=https:// - Linkding__Key= diff --git a/src/Domain/Core/Handler/ILinkdingTagTaskHandler.cs b/src/Domain/Core/Handler/ILinkdingTagTaskHandler.cs new file mode 100644 index 0000000..df04fad --- /dev/null +++ b/src/Domain/Core/Handler/ILinkdingTagTaskHandler.cs @@ -0,0 +1,14 @@ +using System.Threading.Tasks; +using Core.Entities.Linkding; +using LinkdingUpdater.Handler; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; + +namespace Core.Handler +{ + public interface ILinkdingTagTaskHandler + { + string Command { get; } + Task ProcessAsync(Tag tag, ILogger logger, IConfiguration configuration); + } +} \ No newline at end of file diff --git a/src/Linkding/Extensions/StringExtensions.cs b/src/Linkding/Extensions/StringExtensions.cs new file mode 100644 index 0000000..d4b5de4 --- /dev/null +++ b/src/Linkding/Extensions/StringExtensions.cs @@ -0,0 +1,15 @@ +namespace Microsoft.Extensions.DependencyInjection; + +public static class StringExtensions +{ + public static string NormalizeTag(this string tag, int maxTagLength = 64) + { + //in the database only 64 characters are allowed for tags + if (tag.Length >= maxTagLength) + { + tag = tag.Substring(0, (maxTagLength)); + } + + return tag; + } +} \ No newline at end of file diff --git a/src/Linkding/Handler/AddPopularSitesAsTagHandler.cs b/src/Linkding/Handler/AddPopularSitesAsTagHandler.cs index b606486..174beac 100644 --- a/src/Linkding/Handler/AddPopularSitesAsTagHandler.cs +++ b/src/Linkding/Handler/AddPopularSitesAsTagHandler.cs @@ -10,8 +10,6 @@ namespace Linkding.Handler; public class AddPopularSitesAsTagHandler : ILinkdingTaskHandler { - private record RegexExpressionGroups(string Expression, string Replace); - public string Command { get; } = "AddPopularSitesAsTag"; public async Task ProcessAsync(Bookmark bookmark, ILogger logger, IConfiguration configuration) { @@ -34,11 +32,12 @@ public class AddPopularSitesAsTagHandler : ILinkdingTaskHandler var tags = tagsCommaSeparated.Split(','); foreach (var tag in tags) { - if (!string.IsNullOrEmpty(tag) && !returnValue.Instance.TagNames.Contains(tag) && - returnValue.Instance.TagNames.FirstOrDefault(x => x.ToLower() == tag.ToLower()) == null) + var normalizeTag = tag.NormalizeTag(); + if (!string.IsNullOrEmpty(normalizeTag) && !returnValue.Instance.TagNames.Contains(normalizeTag) && + returnValue.Instance.TagNames.FirstOrDefault(x => x.ToLower() == normalizeTag.ToLower()) == null) { - returnValue.Instance.TagNames = returnValue.Instance.TagNames.Add(tag); + returnValue.Instance.TagNames = returnValue.Instance.TagNames.Add(normalizeTag); returnValue.PerformAction = true; returnValue.Action = LinkdingItemAction.Update; } diff --git a/src/Linkding/Handler/NormalizeTagHandler.cs b/src/Linkding/Handler/NormalizeTagHandler.cs new file mode 100644 index 0000000..65d15b2 --- /dev/null +++ b/src/Linkding/Handler/NormalizeTagHandler.cs @@ -0,0 +1,43 @@ +using Core.Entities.Linkding; +using Core.Handler; +using LinkdingUpdater.Handler; + +namespace Linkding.Handler; + +public class NormalizeTagHandler : ILinkdingTaskHandler +{ + public string Command { get; } = "NormalizeTag"; + + public async Task ProcessAsync(Bookmark bookmark, ILogger logger, IConfiguration configuration) + { + var returnValue = new HandlerResult() {Instance = bookmark}; + var update = false; + + var maxTagLength = configuration.GetValue("Worker:TagNameLength"); + + var normalizedTagnames = new List(); + foreach (var tagName in returnValue.Instance.TagNames) + { + if (tagName.Length > maxTagLength) + { + var normalizeTag = tagName.NormalizeTag(); + normalizedTagnames.Add(normalizeTag); + update = true; + } + else + { + normalizedTagnames.Add(tagName); + } + } + + if (update) + { + logger.LogInformation($"Start updating bookmark {returnValue.Instance.WebsiteTitle} - {returnValue.Instance.Id}"); + returnValue.Instance.TagNames = normalizedTagnames; + returnValue.PerformAction = true; + returnValue.Action = LinkdingItemAction.Update; + } + + return returnValue; + } +} \ No newline at end of file diff --git a/src/Linkding/Options/WorkerSettings.cs b/src/Linkding/Options/WorkerSettings.cs index 7b87cfa..60847b7 100644 --- a/src/Linkding/Options/WorkerSettings.cs +++ b/src/Linkding/Options/WorkerSettings.cs @@ -4,5 +4,7 @@ public class WorkerSettings { public const string Position = "Worker"; - public int Intervall { get; set; } = 0; + public int Interval { get; set; } = 0; + public int TagNameLength { get; set; } = 64; + public List Tasks { get; set; } = new List(); } \ No newline at end of file diff --git a/src/Linkding/Program.cs b/src/Linkding/Program.cs index 71d8ee2..ec1ff17 100644 --- a/src/Linkding/Program.cs +++ b/src/Linkding/Program.cs @@ -4,6 +4,7 @@ IHost host = Host.CreateDefaultBuilder(args) .ConfigureServices((ctx, services) => { services.Add_Linkding_HttpClient(ctx.Configuration); + services.Add_Linkding_Worker(ctx.Configuration); services.AddHostedService(); }) .Build(); diff --git a/src/Linkding/Worker.cs b/src/Linkding/Worker.cs index f41a26a..249791d 100644 --- a/src/Linkding/Worker.cs +++ b/src/Linkding/Worker.cs @@ -34,24 +34,24 @@ public class Worker : BackgroundService { _logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now); - await RunTaskHandler(); - int delay = _settings.Intervall * 60000; + await RunBookmarksTaskHandler(); + int delay = _settings.Interval * 60000; if (delay > 0) { - _logger.LogInformation($"Worker paused for: {_settings.Intervall} minutes"); + _logger.LogInformation($"Worker paused for: {_settings.Interval} minutes"); await Task.Delay(delay, stoppingToken); } else { - _logger.LogInformation($"Intervall value is '0' --> stopping worker"); + _logger.LogInformation($"Interval value is '0' --> stopping worker"); _hostApplicationLifetime.StopApplication(); } } } - public async Task RunTaskHandler() + public async Task RunBookmarksTaskHandler() { if (!string.IsNullOrEmpty(_linkdingSettings.Url) && _linkdingSettings.UpdateBookmarks) { @@ -70,6 +70,7 @@ public class Worker : BackgroundService var linkdingBookmarks = await _linkdingService.GetAllBookmarksAsync(); if (linkdingBookmarks.Count() > 0) { + var tasksLowerCase = _settings.Tasks.Select(x => x.ToLower()); _logger.LogInformation($"{linkdingBookmarks.Count()} bookmarks found in {_linkdingSettings.Url}"); @@ -80,6 +81,14 @@ public class Worker : BackgroundService { handlerInstance = (ILinkdingTaskHandler) Activator.CreateInstance(handler); + var task = tasksLowerCase.FirstOrDefault(x => + x.Equals(handlerInstance.Command, StringComparison.InvariantCultureIgnoreCase)); + + if (string.IsNullOrEmpty(task)) + { + continue; + } + foreach (var linkdingBookmark in linkdingBookmarks) { try diff --git a/src/Linkding/config.yml b/src/Linkding/config.yml index fa05237..6f1a483 100644 --- a/src/Linkding/config.yml +++ b/src/Linkding/config.yml @@ -33,7 +33,7 @@ taggingRule: replace: $1 - name: github pattern: https://([ a-zA-Z0-9 ]+)?[ \. ]?(github)\.com[ / ]?([ a-zA-Z0-9\-\+_ ]+)(?:/)?([ a-zA-Z0-9\-\+_ ]+)?(?:/.*)? - replace: $1,$2,$3,$4 + replace: $2,$3,$4 - name: github.io pattern: https://([ a-zA-Z0-9 ]+)\.(github)\.io[ / ]?([ a-zA-Z0-9\-\+_ ]+)(?:/)?([ a-zA-Z0-9\-\+_ ]+)?(?:/.*)? - replace: $1,$2,$3 \ No newline at end of file + replace: $2,$3,$4 \ No newline at end of file diff --git a/src/Wallabag/Options/WorkerSettings.cs b/src/Wallabag/Options/WorkerSettings.cs index d0f868f..3ca3db6 100644 --- a/src/Wallabag/Options/WorkerSettings.cs +++ b/src/Wallabag/Options/WorkerSettings.cs @@ -4,7 +4,7 @@ public class WorkerSettings { public const string Position = "Worker"; - public int Intervall { get; set; } = 0; + public int Interval { get; set; } = 0; public string SyncTag { get; set; } = "readlater"; } \ No newline at end of file diff --git a/src/Wallabag/Worker.cs b/src/Wallabag/Worker.cs index 8d14838..fac68f2 100644 --- a/src/Wallabag/Worker.cs +++ b/src/Wallabag/Worker.cs @@ -42,11 +42,11 @@ public class Worker : BackgroundService _logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now); await RunSyncWallabag(); - int delay = _settings.Intervall * 60000; + int delay = _settings.Interval * 60000; if (delay > 0) { - _logger.LogInformation($"Worker paused for: {_settings.Intervall} minutes"); + _logger.LogInformation($"Worker paused for: {_settings.Interval} minutes"); await Task.Delay(delay, stoppingToken); }