namespace Producer.Features.JobSupport; internal static class ServiceCollectionExtensions { internal static IServiceCollection AddQuartz(this IServiceCollection services, IConfiguration configuration) { var scrapeConfiguration = services.LoadConfigurationFromConfig(configuration, ScrapeConfiguration.SectionName); var githubConfiguration = services.LoadConfigurationFromEnv(); var rabbitConfiguration = services.LoadConfigurationFromEnv(); var jobTypes = Assembly.GetAssembly(typeof(BaseJob)) .GetTypes() .Where(t => t is {IsClass: true, IsAbstract: false} && typeof(IJob).IsAssignableFrom(t) && !Attribute.IsDefined(t, typeof(ManualJobRegistrationAttribute))) .ToList(); foreach (var type in jobTypes) { services.AddTransient(type); } if (!string.IsNullOrEmpty(githubConfiguration.PAT)) { services.AddTransient(); } var openMethod = typeof(ServiceCollectionExtensions).GetMethod(nameof(AddJobWithTrigger), BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance); services.AddQuartz( quartz => { RegisterAutomaticRegistrationJobs(jobTypes, openMethod, quartz, scrapeConfiguration); RegisterDmmJob(githubConfiguration, quartz, scrapeConfiguration); RegisterTorrentioJob(services, quartz, configuration, scrapeConfiguration); RegisterPublisher(quartz, rabbitConfiguration); }); services.AddQuartzHostedService( options => { options.WaitForJobsToComplete = true; }); return services; } private static void RegisterAutomaticRegistrationJobs(List jobTypes, MethodInfo? openMethod, IServiceCollectionQuartzConfigurator quartz, ScrapeConfiguration scrapeConfiguration) { foreach (var jobType in jobTypes) { var key = jobType.GetField("Key")?.GetValue(jobType); var trigger = jobType.GetField("Trigger")?.GetValue(jobType); if (key is null || trigger is null) { Console.WriteLine($"Job {jobType.Name} does not have a JobKey or TriggerKey property"); continue; } var method = openMethod.MakeGenericMethod(jobType); method.Invoke(null, [quartz, key, trigger, scrapeConfiguration]); } } private static void RegisterDmmJob(GithubConfiguration githubConfiguration, IServiceCollectionQuartzConfigurator quartz, ScrapeConfiguration scrapeConfiguration) { if (!string.IsNullOrEmpty(githubConfiguration.PAT)) { AddJobWithTrigger(quartz, SyncDmmJob.Key, SyncDmmJob.Trigger, scrapeConfiguration); } } private static void RegisterTorrentioJob( IServiceCollection services, IServiceCollectionQuartzConfigurator quartz, IConfiguration configuration, ScrapeConfiguration scrapeConfiguration) { var torrentioConfiguration = services.LoadConfigurationFromConfig(configuration, TorrentioConfiguration.SectionName); if (torrentioConfiguration.Instances.Count != 0) { AddJobWithTrigger(quartz, SyncTorrentioJob.Key, SyncTorrentioJob.Trigger, scrapeConfiguration); } } private static void RegisterPublisher(IServiceCollectionQuartzConfigurator quartz, RabbitMqConfiguration rabbitConfig) => AddJobWithTriggerAndInterval(quartz, PublisherJob.Key, PublisherJob.Trigger, rabbitConfig.PublishIntervalInSeconds); private static void AddJobWithTrigger( IServiceCollectionQuartzConfigurator quartz, JobKey key, TriggerKey trigger, ScrapeConfiguration scrapeConfiguration) where TJobType : IJob { var scraper = scrapeConfiguration.Scrapers .FirstOrDefault(x => x.Name != null && x.Name.Equals(typeof(TJobType).Name, StringComparison.OrdinalIgnoreCase)); if (scraper is null || !scraper.Enabled) { return; } quartz.AddJob(opts => opts.WithIdentity(key).StoreDurably()); quartz.AddTrigger( opts => opts .ForJob(key) .WithIdentity(trigger) .StartAt(DateTimeOffset.Now.AddSeconds(20)) .WithSimpleSchedule(x => x.WithInterval(TimeSpan.FromSeconds(scraper.IntervalSeconds)).RepeatForever())); } private static void AddJobWithTriggerAndInterval( IServiceCollectionQuartzConfigurator quartz, JobKey key, TriggerKey trigger, int interval) where TJobType : IJob { quartz.AddJob(opts => opts.WithIdentity(key).StoreDurably()); quartz.AddTrigger( opts => opts .ForJob(key) .WithIdentity(trigger) .StartAt(DateTimeOffset.Now.AddSeconds(20)) .WithSimpleSchedule(x => x.WithInterval(TimeSpan.FromSeconds(interval)).RepeatForever())); } }