Esempio n. 1
0
        public override async Task <JobResult> Execute(SqlService sql, AppSettings settings)
        {
            Utils.Log("Starting Modlist Validation");
            var modlists = await ModlistMetadata.LoadFromGithub();

            using (var queue = new WorkQueue())
            {
                var whitelists = new ValidateModlist();
                await whitelists.LoadListsFromGithub();

                foreach (var list in modlists)
                {
                    try
                    {
                        await ValidateList(sql, list, queue, whitelists);
                    }
                    catch (Exception ex)
                    {
                        Utils.Log(ex.ToString());
                    }
                }
            }

            return(JobResult.Success());
        }
        public async Task CanValidateModLists()
        {
            var sql = Fixture.GetService <SqlService>();

            await using var conn = await sql.Open();

            await conn.ExecuteAsync("DELETE from Patches");

            var modlists = await MakeModList("CanValidateModlistsFile.txt");

            Consts.ModlistMetadataURL = modlists.ToString();
            Utils.Log("Updating modlists");
            await RevalidateLists(true);

            Utils.Log("Checking validated results");
            var data = (await ModlistMetadata.LoadFromGithub()).FirstOrDefault(l => l.Links.MachineURL == "test_list");

            Assert.NotNull(data);
            Assert.Equal(0, data.ValidationSummary.Failed);
            Assert.Equal(1, data.ValidationSummary.Passed);

            await CheckListFeeds(0, 1);

            Utils.Log("Break List");
            var archive = "CanValidateModlistsFile.txt".RelativeTo(Fixture.ServerPublicFolder);
            await archive.MoveToAsync(archive.WithExtension(new Extension(".moved")), true);

            // We can revalidate but the non-nexus archives won't be checked yet since the list didn't change
            await RevalidateLists(false);

            data = (await ModlistMetadata.LoadFromGithub()).FirstOrDefault(l => l.Links.MachineURL == "test_list");
            Assert.NotNull(data);
            Assert.Equal(0, data.ValidationSummary.Failed);
            Assert.Equal(1, data.ValidationSummary.Passed);

            // Run the non-nexus validator
            await RevalidateLists(true);

            data = (await ModlistMetadata.LoadFromGithub()).FirstOrDefault(l => l.Links.MachineURL == "test_list");
            Assert.NotNull(data);
            Assert.Equal(1, data.ValidationSummary.Failed);
            Assert.Equal(0, data.ValidationSummary.Passed);

            await CheckListFeeds(1, 0);

            Utils.Log("Fix List");
            await archive.WithExtension(new Extension(".moved")).MoveToAsync(archive, false);

            await RevalidateLists(true);

            data = (await ModlistMetadata.LoadFromGithub()).FirstOrDefault(l => l.Links.MachineURL == "test_list");
            Assert.NotNull(data);
            Assert.Equal(0, data.ValidationSummary.Failed);
            Assert.Equal(1, data.ValidationSummary.Passed);

            await CheckListFeeds(0, 1);
        }
Esempio n. 3
0
        public async Task CanLoadMetadataFromTestServer()
        {
            var modlist = await MakeModList();

            Consts.ModlistMetadataURL = modlist.ToString();
            var data = await ModlistMetadata.LoadFromGithub();

            Assert.Single(data);
            Assert.Equal("test_list", data.First().Links.MachineURL);
        }
        public async Task CanLoadMetadataFromTestServer()
        {
            var modlist = await MakeModList("CanLoadMetadataFromTestServer.txt");

            Consts.ModlistMetadataURL = modlist.ToString();
            var data = await ModlistMetadata.LoadFromGithub();

            Assert.Equal(3, data.Count);
            Assert.Equal("test_list", data.OrderByDescending(x => x.Links.MachineURL).First().Links.MachineURL);
        }
        public async Task VerifyLogoURLs()
        {
            var modlists = await ModlistMetadata.LoadFromGithub();

            foreach (var modlist in modlists.Select(m => m.Links))
            {
                var logo_state = DownloadDispatcher.ResolveArchive(modlist.ImageUri);
                Assert.IsNotNull(logo_state);
                Assert.IsTrue(await logo_state.Verify(), $"{modlist.ImageUri} is not valid");
            }
        }
        public async Task CanHealLists()
        {
            var modlists = await MakeModList("CanHealLists.txt");

            Consts.ModlistMetadataURL = modlists.ToString();
            Utils.Log("Updating modlists");
            await RevalidateLists(true);

            Utils.Log("Checking validated results");
            var data = (await ModlistMetadata.LoadFromGithub()).FirstOrDefault(l => l.Links.MachineURL == "test_list");

            Assert.NotNull(data);
            Assert.Equal(0, data.ValidationSummary.Failed);
            Assert.Equal(1, data.ValidationSummary.Passed);

            await CheckListFeeds(0, 1);

            Utils.Log("Break List by changing the file");
            var archive = "CanHealLists.txt".RelativeTo(Fixture.ServerPublicFolder);
            await archive.WriteAllTextAsync("broken");

            // We can revalidate but the non-nexus archives won't be checked yet since the list didn't change
            await RevalidateLists(false);

            data = (await ModlistMetadata.LoadFromGithub()).FirstOrDefault(l => l.Links.MachineURL == "test_list");
            Assert.NotNull(data);
            Assert.Equal(0, data.ValidationSummary.Failed);
            Assert.Equal(1, data.ValidationSummary.Passed);

            // Run the non-nexus validator
            await RevalidateLists(true);

            data = (await ModlistMetadata.LoadFromGithub()).FirstOrDefault(l => l.Links.MachineURL == "test_list");
            Assert.NotNull(data);
            Assert.Equal(0, data.ValidationSummary.Failed);
            Assert.Equal(0, data.ValidationSummary.Passed);
            Assert.Equal(1, data.ValidationSummary.Updating);

            var patcher = Fixture.GetService <PatchBuilder>();

            Assert.True(await patcher.Execute() > 1);

            await RevalidateLists(false);

            data = (await ModlistMetadata.LoadFromGithub()).FirstOrDefault(l => l.Links.MachineURL == "test_list");
            Assert.NotNull(data);
            Assert.Equal(0, data.ValidationSummary.Failed);
            Assert.Equal(1, data.ValidationSummary.Passed);
            Assert.Equal(0, data.ValidationSummary.Updating);
        }
Esempio n. 7
0
        public async Task CanValidateModLists()
        {
            await ClearJobQueue();

            var modlists = await MakeModList();

            Consts.ModlistMetadataURL = modlists.ToString();
            Utils.Log("Updating modlists");
            await RevalidateLists();

            Utils.Log("Checking validated results");
            var data = await ModlistMetadata.LoadFromGithub();

            Assert.Single(data);
            Assert.Equal(0, data.First().ValidationSummary.Failed);
            Assert.Equal(1, data.First().ValidationSummary.Passed);

            await CheckListFeeds(0, 1);

            Utils.Log("Break List");
            var archive = "test_archive.txt".RelativeTo(Fixture.ServerPublicFolder);
            await archive.MoveToAsync(archive.WithExtension(new Extension(".moved")), true);

            await RevalidateLists();

            data = await ModlistMetadata.LoadFromGithub();

            Assert.Single(data);
            Assert.Equal(1, data.First().ValidationSummary.Failed);
            Assert.Equal(0, data.First().ValidationSummary.Passed);

            await CheckListFeeds(1, 0);

            Utils.Log("Fix List");
            await archive.WithExtension(new Extension(".moved")).MoveToAsync(archive, false);

            await RevalidateLists();

            data = await ModlistMetadata.LoadFromGithub();

            Assert.Single(data);
            Assert.Equal(0, data.First().ValidationSummary.Failed);
            Assert.Equal(1, data.First().ValidationSummary.Passed);

            await CheckListFeeds(0, 1);
        }
Esempio n. 8
0
        public ModListGalleryVM(MainWindowVM mainWindowVM)
            : base(mainWindowVM)
        {
            MWVM = mainWindowVM;

            Observable.Return(Unit.Default)
            .ObserveOn(RxApp.TaskpoolScheduler)
            .SelectTask(async _ =>
            {
                try
                {
                    Error    = null;
                    var list = await ModlistMetadata.LoadFromGithub();
                    return(list.AsObservableChangeSet(x => x.DownloadMetadata?.Hash ?? $"Fallback{missingHashFallbackCounter++}"));
                }
                catch (Exception ex)
                {
                    Utils.Error(ex);
                    Error = ErrorResponse.Fail(ex);
                    return(Observable.Empty <IChangeSet <ModlistMetadata, string> >());
                }
            })
            // Unsubscribe and release when not active
            .FlowSwitch(
                this.WhenAny(x => x.IsActive),
                valueWhenOff: Observable.Return(ChangeSet <ModlistMetadata, string> .Empty))
            // Convert to VM and bind to resulting list
            .Switch()
            .ObserveOnGuiThread()
            .Transform(m => new ModListMetadataVM(this, m))
            .DisposeMany()
            .Bind(ModLists)
            .Subscribe()
            .DisposeWith(CompositeDisposable);

            // Extra GC when navigating away, just to immediately clean up modlist metadata
            this.WhenAny(x => x.IsActive)
            .Where(x => !x)
            .Skip(1)
            .Delay(TimeSpan.FromMilliseconds(50), RxApp.MainThreadScheduler)
            .Subscribe(_ =>
            {
                GC.Collect();
            })
            .DisposeWith(CompositeDisposable);
        }
Esempio n. 9
0
        public override async Task <JobResult> Execute(DBContext db, AppSettings settings)
        {
            Utils.Log("Starting ModList indexing");
            var modlists = await ModlistMetadata.LoadFromGithub();

            using (var queue = new WorkQueue())
            {
                foreach (var list in modlists)
                {
                    try
                    {
                        await EnqueueFromList(db, list, queue);
                    }
                    catch (Exception ex)
                    {
                        Utils.Log(ex.ToString());
                    }
                }
            }

            return(JobResult.Success());
        }
Esempio n. 10
0
        public ModListGalleryVM(MainWindowVM mainWindowVM)
        {
            MWVM        = mainWindowVM;
            BackCommand = ReactiveCommand.Create(
                execute: () => mainWindowVM.ActivePane = mainWindowVM.ModeSelectionVM);
            RefreshCommand = ReactiveCommand.Create(() => { });

            RefreshCommand.StartingExecution()
            .StartWith(Unit.Default)
            .ObserveOn(RxApp.TaskpoolScheduler)
            .SelectTask(async _ =>
            {
                return((await ModlistMetadata.LoadFromGithub())
                       .AsObservableChangeSet(x => x.DownloadMetadata?.Hash ?? $"Fallback{missingHashFallbackCounter++}"));
            })
            .Switch()
            .ObserveOnGuiThread()
            .Transform(m => new ModListMetadataVM(this, m))
            .Bind(ModLists)
            .Subscribe()
            .DisposeWith(CompositeDisposable);
        }
Esempio n. 11
0
        public async Task <string[]> GetUsedCDNFiles()
        {
            var modlists = (await ModlistMetadata.LoadFromGithub())
                           .Concat((await ModlistMetadata.LoadUnlistedFromGithub()))
                           .Select(f => f.Links.Download)
                           .Where(f => f.StartsWith(Consts.WabbajackAuthoredFilesPrefix))
                           .Select(f => f.Substring(Consts.WabbajackAuthoredFilesPrefix.Length));

            var files = (await _sql.ModListArchives())
                        .Select(a => a.State)
                        .OfType <WabbajackCDNDownloader.State>()
                        .Select(s => s.Url.ToString().Substring(Consts.WabbajackAuthoredFilesPrefix.Length));



            var names     = modlists.Concat(files).Distinct().ToArray();
            var namesBoth = names.Concat(names.Select(HttpUtility.UrlDecode))
                            .Concat(names.Select(HttpUtility.UrlEncode))
                            .Distinct()
                            .ToArray();

            return(namesBoth);
        }
        public void TestLoadingModlists()
        {
            var modlists = ModlistMetadata.LoadFromGithub();

            Assert.IsTrue(modlists.Count > 0);
        }
Esempio n. 13
0
        public async Task TestLoadingModlists()
        {
            var modlists = await ModlistMetadata.LoadFromGithub();

            Assert.IsTrue(modlists.Count > 0);
        }
Esempio n. 14
0
        public override async Task <int> Execute()
        {
            int downloaded = 0;
            var lists      = (await ModlistMetadata.LoadFromGithub())
                             .Concat(await ModlistMetadata.LoadUnlistedFromGithub()).ToList();

            foreach (var list in lists)
            {
                try
                {
                    ReportStarting(list.Links.MachineURL);
                    if (await _sql.HaveIndexedModlist(list.Links.MachineURL, list.DownloadMetadata.Hash))
                    {
                        continue;
                    }


                    if (!_maintainer.HaveArchive(list.DownloadMetadata !.Hash))
                    {
                        _logger.Log(LogLevel.Information, $"Downloading {list.Links.MachineURL}");
                        await _discord.Send(Channel.Ham,
                                            new DiscordMessage
                        {
                            Content = $"Downloading {list.Links.MachineURL} - {list.DownloadMetadata.Hash}"
                        });

                        var tf    = new TempFile();
                        var state = DownloadDispatcher.ResolveArchive(list.Links.Download);
                        if (state == null)
                        {
                            _logger.Log(LogLevel.Error,
                                        $"Now downloader found for list {list.Links.MachineURL} : {list.Links.Download}");
                            continue;
                        }

                        downloaded += 1;
                        await state.Download(new Archive(state) { Name = $"{list.Links.MachineURL}.wabbajack" }, tf.Path);

                        var hash = await tf.Path.FileHashAsync();

                        if (hash != list.DownloadMetadata.Hash)
                        {
                            _logger.Log(LogLevel.Error,
                                        $"Downloaded modlist {list.Links.MachineURL} {list.DownloadMetadata.Hash} didn't match metadata hash of {hash}");
                            await _sql.IngestModList(list.DownloadMetadata.Hash, list, new ModList(), true);

                            continue;
                        }

                        await _maintainer.Ingest(tf.Path);
                    }

                    _maintainer.TryGetPath(list.DownloadMetadata.Hash, out var modlistPath);
                    ModList modlist;
                    await using (var fs = await modlistPath.OpenRead())
                        using (var zip = new ZipArchive(fs, ZipArchiveMode.Read))
                            await using (var entry = zip.GetEntry("modlist")?.Open())
                            {
                                if (entry == null)
                                {
                                    _logger.LogWarning($"Bad Modlist {list.Links.MachineURL}");
                                    await _discord.Send(Channel.Ham,
                                                        new DiscordMessage
                                    {
                                        Content = $"Bad Modlist  {list.Links.MachineURL} - {list.DownloadMetadata.Hash}"
                                    });

                                    continue;
                                }

                                try
                                {
                                    modlist = entry.FromJson <ModList>();
                                }
                                catch (JsonReaderException)
                                {
                                    _logger.LogWarning($"Bad Modlist {list.Links.MachineURL}");
                                    await _discord.Send(Channel.Ham,
                                                        new DiscordMessage
                                    {
                                        Content = $"Bad Modlist  {list.Links.MachineURL} - {list.DownloadMetadata.Hash}"
                                    });

                                    continue;
                                }
                            }

                    await _sql.IngestModList(list.DownloadMetadata !.Hash, list, modlist, false);
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, $"Error downloading modlist {list.Links.MachineURL}");
                    await _discord.Send(Channel.Ham,
                                        new DiscordMessage
                    {
                        Content =
                            $"Error downloading modlist {list.Links.MachineURL} - {list.DownloadMetadata.Hash}"
                    });
                }
                finally
                {
                    ReportEnding(list.Links.MachineURL);
                }
            }
            _logger.Log(LogLevel.Information, $"Done checking modlists. Downloaded {downloaded} new lists");
            if (downloaded > 0)
            {
                await _discord.Send(Channel.Ham,
                                    new DiscordMessage { Content = $"Downloaded {downloaded} new lists" });
            }

            var fc = await _sql.EnqueueModListFilesForIndexing();

            _logger.Log(LogLevel.Information, $"Enqueing {fc} files for downloading");
            if (fc > 0)
            {
                await _discord.Send(Channel.Ham,
                                    new DiscordMessage { Content = $"Enqueing {fc} files for downloading" });
            }

            return(downloaded);
        }
Esempio n. 15
0
        public ModListGalleryVM(MainWindowVM mainWindowVM)
            : base(mainWindowVM)
        {
            MWVM = mainWindowVM;

            ClearFiltersCommand = ReactiveCommand.Create(
                () =>
            {
                OnlyInstalled = false;
                Search        = string.Empty;
            });

            var random     = new Random();
            var sourceList = Observable.Return(Unit.Default)
                             .ObserveOn(RxApp.TaskpoolScheduler)
                             .SelectTask(async _ =>
            {
                try
                {
                    Error    = null;
                    var list = await ModlistMetadata.LoadFromGithub();
                    Error    = ErrorResponse.Success;
                    return(list
                           // Sort randomly initially, just to give each list a fair shake
                           .Shuffle(random)
                           .AsObservableChangeSet(x => x.DownloadMetadata?.Hash ?? $"Fallback{missingHashFallbackCounter++}"));
                }
                catch (Exception ex)
                {
                    Utils.Error(ex);
                    Error = ErrorResponse.Fail(ex);
                    return(Observable.Empty <IChangeSet <ModlistMetadata, string> >());
                }
            })
                             // Unsubscribe and release when not active
                             .FlowSwitch(
                this.WhenAny(x => x.IsActive),
                valueWhenOff: Observable.Return(ChangeSet <ModlistMetadata, string> .Empty))
                             .Switch()
                             .RefCount();

            _Loaded = sourceList.CollectionCount()
                      .Select(c => c > 0)
                      .ToProperty(this, nameof(Loaded));

            // Convert to VM and bind to resulting list
            sourceList
            .ObserveOnGuiThread()
            .Transform(m => new ModListMetadataVM(this, m))
            .DisposeMany()
            // Filter only installed
            .Filter(predicateChanged: this.WhenAny(x => x.OnlyInstalled)
                    .Select <bool, Func <ModListMetadataVM, bool> >(onlyInstalled => (vm) =>
            {
                if (!onlyInstalled)
                {
                    return(true);
                }
                if (!GameRegistry.Games.TryGetValue(vm.Metadata.Game, out var gameMeta))
                {
                    return(false);
                }
                return(gameMeta.IsInstalled);
            }))
            // Filter on search box
            .Filter(predicateChanged: this.WhenAny(x => x.Search)
                    .Debounce(TimeSpan.FromMilliseconds(150), RxApp.MainThreadScheduler)
                    .Select <string, Func <ModListMetadataVM, bool> >(search => (vm) =>
            {
                if (string.IsNullOrWhiteSpace(search))
                {
                    return(true);
                }
                return(vm.Metadata.Title.ContainsCaseInsensitive(search));
            }))
            // Put broken lists at bottom
            .Sort(Comparer <ModListMetadataVM> .Create((a, b) => a.IsBroken.CompareTo(b.IsBroken)))
            .Bind(ModLists)
            .Subscribe()
            .DisposeWith(CompositeDisposable);

            // Extra GC when navigating away, just to immediately clean up modlist metadata
            this.WhenAny(x => x.IsActive)
            .Where(x => !x)
            .Skip(1)
            .Delay(TimeSpan.FromMilliseconds(50), RxApp.MainThreadScheduler)
            .Subscribe(_ =>
            {
                GC.Collect();
            })
            .DisposeWith(CompositeDisposable);
        }
Esempio n. 16
0
        public ModListGalleryVM(MainWindowVM mainWindowVM)
            : base(mainWindowVM)
        {
            MWVM = mainWindowVM;

            // load persistent filter settings
            if (settings.IsPersistent)
            {
                GameType         = !string.IsNullOrEmpty(settings.Game) ? settings.Game : ALL_GAME_TYPE;
                ShowNSFW         = settings.ShowNSFW;
                ShowUtilityLists = settings.ShowUtilityLists;
                OnlyInstalled    = settings.OnlyInstalled;
                Search           = settings.Search;
            }
            else
            {
                GameType = ALL_GAME_TYPE;
            }

            // subscribe to save signal
            MWVM.Settings.SaveSignal
            .Subscribe(_ => UpdateFiltersSettings())
            .DisposeWith(this.CompositeDisposable);

            ClearFiltersCommand = ReactiveCommand.Create(
                () =>
            {
                OnlyInstalled    = false;
                ShowNSFW         = false;
                ShowUtilityLists = false;
                Search           = string.Empty;
                GameType         = ALL_GAME_TYPE;
            });


            this.WhenAny(x => x.OnlyInstalled)
            .Subscribe(val =>
            {
                if (val)
                {
                    GameType = ALL_GAME_TYPE;
                }
            })
            .DisposeWith(CompositeDisposable);

            var sourceList = Observable.Return(Unit.Default)
                             .ObserveOn(RxApp.TaskpoolScheduler)
                             .SelectTask(async _ =>
            {
                try
                {
                    Error    = null;
                    var list = await ModlistMetadata.LoadFromGithub();
                    Error    = ErrorResponse.Success;
                    return(list
                           .AsObservableChangeSet(x => x.DownloadMetadata?.Hash ?? Hash.Empty));
                }
                catch (Exception ex)
                {
                    Utils.Error(ex);
                    Error = ErrorResponse.Fail(ex);
                    return(Observable.Empty <IChangeSet <ModlistMetadata, Hash> >());
                }
            })
                             // Unsubscribe and release when not active
                             .FlowSwitch(
                this.WhenAny(x => x.IsActive),
                valueWhenOff: Observable.Return(ChangeSet <ModlistMetadata, Hash> .Empty))
                             .Switch()
                             .RefCount();

            _Loaded = sourceList.CollectionCount()
                      .Select(c => c > 0)
                      .ToProperty(this, nameof(Loaded));

            // Convert to VM and bind to resulting list
            sourceList
            .ObserveOnGuiThread()
            .Transform(m => new ModListMetadataVM(this, m))
            .DisposeMany()
            // Filter only installed
            .Filter(this.WhenAny(x => x.OnlyInstalled)
                    .Select <bool, Func <ModListMetadataVM, bool> >(onlyInstalled => (vm) =>
            {
                if (!onlyInstalled)
                {
                    return(true);
                }
                if (!GameRegistry.Games.TryGetValue(vm.Metadata.Game, out var gameMeta))
                {
                    return(false);
                }
                return(gameMeta.IsInstalled);
            }))
            // Filter on search box
            .Filter(this.WhenAny(x => x.Search)
                    .Debounce(TimeSpan.FromMilliseconds(150), RxApp.MainThreadScheduler)
                    .Select <string, Func <ModListMetadataVM, bool> >(search => (vm) =>
            {
                if (string.IsNullOrWhiteSpace(search))
                {
                    return(true);
                }
                return(vm.Metadata.Title.ContainsCaseInsensitive(search) || vm.Metadata.tags.Any(t => t.ContainsCaseInsensitive(search)));
            }))
            .Filter(this.WhenAny(x => x.ShowNSFW)
                    .Select <bool, Func <ModListMetadataVM, bool> >(showNSFW => vm =>
            {
                if (!vm.Metadata.NSFW)
                {
                    return(true);
                }
                return(vm.Metadata.NSFW && showNSFW);
            }))
            .Filter(this.WhenAny(x => x.ShowUtilityLists)
                    .Select <bool, Func <ModListMetadataVM, bool> >(showUtilityLists => vm => showUtilityLists ? vm.Metadata.UtilityList : !vm.Metadata.UtilityList))
            // Filter by Game
            .Filter(this.WhenAny(x => x.GameType)
                    .Debounce(TimeSpan.FromMilliseconds(150), RxApp.MainThreadScheduler)
                    .Select <string, Func <ModListMetadataVM, bool> >(GameType => (vm) =>
            {
                if (GameType == ALL_GAME_TYPE)
                {
                    return(true);
                }
                if (string.IsNullOrEmpty(GameType))
                {
                    return(false);
                }

                return(GameType == vm.Metadata.Game.GetDescription <Game>().ToString());
            }))
            .Bind(ModLists)
            .Subscribe()
            .DisposeWith(CompositeDisposable);

            // Extra GC when navigating away, just to immediately clean up modlist metadata
            this.WhenAny(x => x.IsActive)
            .Where(x => !x)
            .Skip(1)
            .Delay(TimeSpan.FromMilliseconds(50), RxApp.MainThreadScheduler)
            .Subscribe(_ =>
            {
                GC.Collect();
            })
            .DisposeWith(CompositeDisposable);
        }