Beispiel #1
0
        public async Task ReloadContainerCache()
        {
            await Context.Channel.SendMessageAsync("**[Admin]** OK, reloading ContainerCache");

            ContainerCache.Dispose();
            ContainerCache.Initialize(Program.LOCAL_CONTAINER_CACHE_DIRECTORY);
        }
        private void RenewCache()
        {
            _localDataCache = _localDataCache ?? MemoryContext.DataSegment.LocalData;
            _localDataCache.TakeSnapshot();

            var objMgr = MemoryContext.DataSegment.ObjectManager;

            _objMgrCache = _objMgrCache ?? objMgr;

            if ((objMgr == null && _objMgrCache != null) ||
                (objMgr.Address != _objMgrCache.Address))
            {
                _playerCache    = null;
                _acdCache       = null;
                _attributeCache = null;
                _worldCache     = null;
                _sceneCache     = null;
                return;
            }

            _objMgrCache.TakeSnapshot();

            _playerCache = _playerCache ?? _objMgrCache.Player;
            _playerCache.TakeSnapshot();

            (_acdCache = _acdCache ?? new ContainerCache <MemoryModel.Core.ACD>(_objMgrCache.ACDManager.ActorCommonData)).Update();
            (_actorCache = _actorCache ?? new ContainerCache <MemoryModel.Core.Actor>(_objMgrCache.Actors)).Update();
            (_attributeCache = _attributeCache ?? new AttributeCache(MemoryContext, _objMgrCache.FastAttrib)).Update();
            (_worldCache = _worldCache ?? new ContainerCache <MemoryModel.Core.World>(_objMgrCache.Worlds)).Update();
            (_sceneCache = _sceneCache ?? new ContainerCache <MemoryModel.Core.Scene>(_objMgrCache.Scenes)).Update();

            AttributeCache.Current = _attributeCache;
        }
Beispiel #3
0
        public async Task RebuildIndex()
        {
            await Context.Channel.SendMessageAsync("**[Admin]** OK, building index");

            // Create a dummy ContainerIndex using latest data
            ContainerIndex containerIndex = new ContainerIndex();

            containerIndex.Event     = StrippedContainer.ConvertToStrippedContainer(ContainerCache.GetEvents().First());
            containerIndex.LineNews  = StrippedContainer.ConvertToStrippedContainer(ContainerCache.GetLineNews().First());
            containerIndex.PopUpNews = StrippedContainer.ConvertToStrippedContainer(ContainerCache.GetPopUpNews().First());
            containerIndex.Present   = StrippedContainer.ConvertToStrippedContainer(ContainerCache.GetPresents().First());

            // Acquire the WebFileHandler lock
            lock (WebFileHandler.Lock)
            {
                // Connect
                WebFileHandler.Connect(((SsbuBotConfiguration)Configuration.LoadedConfiguration).WebConfig);

                // Write the file
                WebFileHandler.WriteAllText(((SsbuBotConfiguration)Configuration.LoadedConfiguration).WebConfig.ContainerIndexPath, WebFileHandler.ToJson(containerIndex));

                // Disconnect
                WebFileHandler.Disconnect();
            }

            await Context.Channel.SendMessageAsync("**[Admin]** Done");
        }
Beispiel #4
0
        public async Task AnnounceToChannel(string type, string id)
        {
            switch (type)
            {
            case "event":
                await EventDiscordHandler.HandleAdded(ContainerCache.GetEventWithId(id));

                break;

            case "linenews":
                await LineNewsDiscordHandler.HandleAdded(ContainerCache.GetLineNewsWithId(id));

                break;

            case "popupnews":
                await PopUpNewsDiscordHandler.HandleAdded(ContainerCache.GetPopUpNewsWithId(id));

                break;

            case "present":
                await PresentDiscordHandler.HandleAdded(ContainerCache.GetPresentWithId(id));

                break;

            default:
                throw new Exception("Invalid type (must be event, linenews, popupnews, or present)");
            }

            await Context.Channel.SendMessageAsync("**[Admin]** OK, notified");
        }
Beispiel #5
0
        public void Dispose()
        {
            if (_wasDisposed)
            {
                return;
            }
            _wasDisposed = true;

            if (Role == ContainerRole.Root)
            {
                _singletons.DisposeAndClear();
            }

            ContainerCache.DisposeAndClear();


            _transients.DisposeAndClear();
            _pluginGraph.SafeDispose();

            if (Role == ContainerRole.Root)
            {
                _profiles.AllProfiles().Each(x => x.Dispose());
            }

            _trackedDisposables.Each(x => x.Dispose());
        }
Beispiel #6
0
        public async Task RebuildList(string type)
        {
            await Context.Channel.SendMessageAsync("**[Admin]** OK, building list");

            // Create a new StrippedContainer list
            List <StrippedContainer> containerList = new List <StrippedContainer>();

            // Declare a variable to hold the path
            string indexPath = ((SsbuBotConfiguration)Configuration.LoadedConfiguration).WebConfig.ContainerListPath;

            switch (type)
            {
            case "event":
                ContainerCache.GetEvents().ForEach(x => containerList.Add(StrippedContainer.ConvertToStrippedContainer(x)));
                indexPath = string.Format(indexPath, "event");

                break;

            case "linenews":
                ContainerCache.GetLineNews().ForEach(x => containerList.Add(StrippedContainer.ConvertToStrippedContainer(x)));
                indexPath = string.Format(indexPath, "line_news");

                break;

            case "popupnews":
                ContainerCache.GetPopUpNews().ForEach(x => containerList.Add(StrippedContainer.ConvertToStrippedContainer(x)));
                indexPath = string.Format(indexPath, "popup_news");

                break;

            case "present":
                ContainerCache.GetPresents().ForEach(x => containerList.Add(StrippedContainer.ConvertToStrippedContainer(x)));
                indexPath = string.Format(indexPath, "present");

                break;

            default:
                throw new Exception("Invalid type (must be event, linenews, popupnews, or present)");
            }

            // Acquire the WebFileHandler lock
            lock (WebFileHandler.Lock)
            {
                // Connect
                WebFileHandler.Connect(((SsbuBotConfiguration)Configuration.LoadedConfiguration).WebConfig);

                // Upload the file
                WebFileHandler.WriteAllText(indexPath, WebFileHandler.ToJson(containerList));

                // Disconnect
                WebFileHandler.Disconnect();
            }

            await Context.Channel.SendMessageAsync("**[Admin]** Done");
        }
Beispiel #7
0
        public static async Task ListContainers(FileType fileType, Language language, SocketCommandContext context)
        {
            // Create a list of IDs and their names
            List <string> ids = new List <string>();

            switch (fileType)
            {
            case FileType.Event:
                foreach (Event smashEvent in ContainerCache.GetEvents())
                {
                    ids.Add($"``{smashEvent.Id}`` - {smashEvent.TitleText[language]}");
                }

                break;

            case FileType.LineNews:
                foreach (LineNews lineNews in ContainerCache.GetLineNews())
                {
                    ids.Add($"``{lineNews.Id}``");
                }

                break;

            case FileType.PopUpNews:
                // TODO: hack something better can be made, pages maybe?
                foreach (PopUpNews news in ContainerCache.GetPopUpNews().Where(x => !x.Id.Contains("_")).Take(10))
                {
                    ids.Add($"``{news.Id}`` - {news.TitleText[language]}");
                }

                ids.Add("(only the 10 most recent Pop-Up News is displayed)");

                break;

            case FileType.Present:
                foreach (Present present in ContainerCache.GetPresents())
                {
                    ids.Add($"``{present.Id}`` - {present.TitleText[language]}");
                }

                break;

            default:
                throw new LocalizedException("list.unknown_file_type");
            }

            Embed embed = new EmbedBuilder()
                          .WithTitle(Localizer.Localize("list.title", language))
                          .WithDescription(Localizer.Localize("list.description", language) + string.Join('\n', ids) + "")
                          .WithColor(Color.Green)
                          .Build();

            await context.Channel.SendMessageAsync(embed : embed);
        }
        protected virtual async Task <FileContainerConfiguration> GetConfigurationAsync(string name)
        {
            var cacheItem = await ContainerCache.GetOrAddAsync(
                name,
                async() =>
            {
                var container = await FileStoringContainerRepository.FindByNameAsync(name, true);
                return(container?.AsCacheItem());
            });

            return(cacheItem == null ? null : FileContainerConfigurationConverter.ToConfiguration(cacheItem));
        }
Beispiel #9
0
        public async Task Execute(string id = null, string languageString = null)
        {
            // Get the language
            Language language = DiscordUtil.GetDefaultLanguage(Context.Guild, languageString);

            // Check for no ID
            if (id == null)
            {
                await ListCommand.ListContainers(FileType.LineNews, language, Context);

                return;
            }

            // Get the PopUpNews with this ID
            LineNews lineNews = ContainerCache.GetLineNewsWithId(id);

            // Check if this exists
            if (lineNews == null)
            {
                throw new LocalizedException("line_news.not_found");
            }

            // Localize the title
            string titleKey       = "line_news.title";
            string localizedTitle = string.Format(Localizer.Localize(titleKey, language), lineNews.Id);

            // Localize the description
            string descriptionKey       = "line_news.description";
            string localizedDescription = string.Format(Localizer.Localize(descriptionKey, language), $"https://smash.oatmealdome.me/line_news/{lineNews.Id}/{language.GetCode()}/");

            // Localize the line field name
            string lineFieldNameKey       = "line_news.line_title";
            string localizedLineFieldName = Localizer.Localize(lineFieldNameKey, language);

            // Construct the Embed
            EmbedBuilder embedBuilder = new EmbedBuilder()
                                        .WithTitle(localizedTitle)
                                        //.WithDescription(localizedDescription)
                                        .AddField(Localizer.Localize("line_news.start_time", language), Localizer.LocalizeDateTime(lineNews.StartDateTime, language), true)
                                        .AddField(Localizer.Localize("line_news.end_time", language), Localizer.LocalizeDateTime(lineNews.EndDateTime, language), true);

            // Add every OneLine
            foreach (OneLine oneLine in lineNews.OneLines)
            {
                embedBuilder.AddField(string.Format(localizedLineFieldName, oneLine.Id), oneLine.Text[language]);
            }

            await Context.Channel.SendMessageAsync(embed : embedBuilder.Build());
        }
 private void Reset()
 {
     _minimapItemsDic.Clear();
     if (_minimapItems.Count > 0)
     {
         Execute.OnUIThread(() => _minimapItems.Clear());
     }
     _acdsObserver = null;
     _playerAcd    = null;
     _ignoredSnoIds.Clear();
     _localData         = null;
     _objectManager     = null;
     _isLocalActorReady = false;
     _previousFrame     = 0;
 }
        protected override async Task RunAppSpecificBootTasks()
        {
            // Create the cache directories if needed
            System.IO.Directory.CreateDirectory(Program.LOCAL_CONTAINER_CACHE_DIRECTORY);
            System.IO.Directory.CreateDirectory(Program.LOCAL_COMMON_CACHE_DIRECTORY);

            // Initialize the ContainerCache
            ContainerCache.Initialize(Program.LOCAL_CONTAINER_CACHE_DIRECTORY);

            await Task.FromResult(0);

            /*await DiscordBot.LoggingChannel.SendMessageAsync($"**[BootHousekeepingJob]** Scheduling archivals");
             *
             * // Archive all Container pages in production mode
             * if (Configuration.LoadedConfiguration.IsProduction)
             * {
             *  // Declare the minutes offset to stagger the archival jobs
             *  int minutesOffset = 1;
             *
             *  // Get the current DateTime in UTC
             *  DateTime nowTime = DateTime.UtcNow;
             *
             *  async Task ScheduleArchivalIfEligible(Nintendo.SmashUltimate.Bcat.Container container)
             *  {
             *      // Check if the Container's page is active
             *      if (container.StartDateTime < nowTime && container.EndDateTime > nowTime)
             *      {
             *          // Schedule the Job
             *          if (await ContainerArchivalHandler.ScheduleArchival(container, nowTime.AddMinutes(minutesOffset)))
             *          {
             *              // Increment the minutes
             *              minutesOffset++;
             *          }
             *      }
             *  }
             *
             *  // Schedule for all PopUpNews and Events
             *  foreach (PopUpNews popUpNews in ContainerCache.GetPopUpNews())
             *  {
             *      await ScheduleArchivalIfEligible(popUpNews);
             *  }
             *
             *  foreach (Event smashEvent in ContainerCache.GetEvents())
             *  {
             *      await ScheduleArchivalIfEligible(smashEvent);
             *  }
             * }*/
        }
Beispiel #12
0
        public async Task Execute(string id = null, string languageString = null)
        {
            // Get the language
            Language language = DiscordUtil.GetDefaultLanguage(Context.Guild, languageString);

            // Check for no ID
            if (id == null)
            {
                await ListCommand.ListContainers(FileType.Present, language, Context);

                return;
            }

            // Get the Present with this ID
            Present present = ContainerCache.GetPresentWithId(id);

            // Check if this exists
            if (present == null)
            {
                throw new LocalizedException("present.not_found");
            }

            // Localize the description
            string localizedDescription = string.Format(Localizer.Localize("present.description", language), present.ContentText[language], $"https://smash.oatmealdome.me/present/{present.Id}/{language.GetCode()}/");

            // Construct the image URL
            string url = $"https://cdn.oatmealdome.me/smash/present/{present.Id}/image.jpg";

            // Construct the Embed
            Embed embed = new EmbedBuilder()
                          .WithTitle(present.TitleText[language])
                          .WithDescription(localizedDescription)
                          .AddField(Localizer.Localize("present.start_time", language), Localizer.LocalizeDateTime(present.StartDateTime, language), true)
                          .AddField(Localizer.Localize("present.end_time", language), Localizer.LocalizeDateTime(present.EndDateTime, language), true)
                          .WithImageUrl(url)
                          .Build();

            await Context.Channel.SendMessageAsync(embed : embed);
        }
Beispiel #13
0
        public async Task UploadCacheToS3()
        {
            await Context.Channel.SendMessageAsync("**[Admin]** OK, building list");

            List <Container> allContainers = new List <Container>();

            // Add all Containers to the List
            allContainers.AddRange(ContainerCache.GetEvents());
            allContainers.AddRange(ContainerCache.GetLineNews());
            allContainers.AddRange(ContainerCache.GetPopUpNews());
            allContainers.AddRange(ContainerCache.GetPresents());

            // Loop over every Container
            foreach (Container container in allContainers)
            {
                await Context.Channel.SendMessageAsync("**[Admin]** Uploading " + container.GetType().Name + " with ID " + container.Id);

                ContainerWebHandler.HandleContainer(container);
            }

            await Context.Channel.SendMessageAsync("**[Admin]** Done");
        }
        protected override async Task Run()
        {
            await DiscordBot.LoggingChannel.SendMessageAsync($"**[WebPConversionOneTimeTask]** Starting WebP image conversion");

            List <Container> allContainers = new List <Container>();

            // Add all Containers to the List
            allContainers.AddRange(ContainerCache.GetEvents());
            allContainers.AddRange(ContainerCache.GetPopUpNews());
            allContainers.AddRange(ContainerCache.GetPresents());

            // Loop over every Container
            foreach (Container container in allContainers)
            {
                // Get the FileType
                FileType fileType = FileTypeExtensions.GetTypeFromContainer(container);

                // Format the destination S3 path
                string s3Path = $"/smash/{FileTypeExtensions.GetNamePrefixFromType(fileType)}/{container.Id}";

                // Get the raw image
                byte[] jpgImage = (byte[])container.GetType().GetProperty("Image").GetValue(container);

                // Create a new MagickImage
                using (MagickImage image = new MagickImage(jpgImage))
                {
                    // Set the output format to WebP
                    image.Format = MagickFormat.WebP;

                    // Create the raw WebP
                    byte[] webpImage = image.ToByteArray();

                    await DiscordBot.LoggingChannel.SendMessageAsync($"**[WebPConversionOneTimeTask]** Uploading image for {fileType.ToString()} ID {container.Id}");

                    // Upload to S3
                    S3Api.TransferFile(webpImage, s3Path, "image.webp", "image/webp");
                }
            }
        }
Beispiel #15
0
        public void Update(MemoryContext ctx)
        {
            if (ctx == null)
            {
                throw new ArgumentNullException(nameof(ctx));
            }
            ctx.Memory.Reader.ResetCounters();
            try
            {
                if (!IsLocalActorValid(ctx))
                {
                    return;
                }

                if (!IsObjectManagerOnNewFrame(ctx))
                {
                    return;
                }

                var itemsToAdd    = new List <IMapMarker>();
                var itemsToRemove = new List <IMapMarker>();

                _acdsObserver = _acdsObserver ?? new ContainerCache <ACD>(ctx.DataSegment.ObjectManager.ACDManager.ActorCommonData);
                _acdsObserver.Update();

                // Must have a local ACD to base coords on.
                if (_playerAcd == null)
                {
                    var playerAcdId = ctx.DataSegment.ObjectManager.PlayerDataManager[
                        ctx.DataSegment.ObjectManager.Player.LocalPlayerIndex].ACDID;

                    _playerAcd = _acdsObserver.Items[(short)playerAcdId];
                }


                foreach (var acd in ctx.DataSegment.ObjectManager.ACDManager.ActorCommonData)
                {
                    Console.WriteLine(acd.Name);

                    if (!((acd.MonsterQuality == MonsterQuality.Champion) || (acd.MonsterQuality == MonsterQuality.Rare) || (acd.MonsterQuality == MonsterQuality.Boss)))
                    {
                        continue;
                    }



                    if (WindowsInput.InputSimulator.IsKeyDown(VirtualKeyCode.TAB))
                    {
                        System.Drawing.Point MouseCords = D3ToScreen.FromD3toScreenCoords(acd.Position, _playerAcd.Position);
                        if ((MouseCords.X > 0 && MouseCords.Y > 0) && (MouseCords.X < System.Windows.Forms.Screen.PrimaryScreen.Bounds.Width && MouseCords.Y < System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height))
                        {
                            SetCursorPos(MouseCords.X, MouseCords.Y);
                            Thread.Sleep(600);
                        }
                        //System.Windows.MessageBox.Show("X " + MouseCords.X);
                    }
                }
            }
            catch (Exception exception)
            {
                OnUpdateException(exception);
            }
        }
        public void Update(MemoryContext ctx)
        {
            if (ctx == null)
            {
                throw new ArgumentNullException(nameof(ctx));
            }
            ctx.Memory.Reader.ResetCounters();
            try
            {
                if (!IsLocalActorValid(ctx))
                {
                    return;
                }

                if (!IsObjectManagerOnNewFrame(ctx))
                {
                    return;
                }

                var itemsToAdd    = new List <IMapMarker>();
                var itemsToRemove = new List <IMapMarker>();

                _acdsObserver = _acdsObserver ?? new ContainerCache <ACD>(ctx.DataSegment.ObjectManager.ACDManager.ActorCommonData);
                _acdsObserver.Update();

                // Must have a local ACD to base coords on.
                if (_playerAcd == null)
                {
                    var playerAcdId = ctx.DataSegment.ObjectManager.PlayerDataManager[
                        ctx.DataSegment.ObjectManager.Player.LocalPlayerIndex].ACDID;

                    _playerAcd = _acdsObserver.Items[(short)playerAcdId];
                }

                foreach (var acd in _acdsObserver.OldItems)
                {
                    var marker = default(IMapMarker);
                    if (_minimapItemsDic.TryGetValue(acd.Address, out marker))
                    {
                        Trace.WriteLine("Removing " + acd.Name);
                        itemsToRemove.Add(marker);
                    }
                }

                foreach (var acd in _acdsObserver.NewItems)
                {
                    var actorSnoId = acd.ActorSNO;
                    if (_ignoredSnoIds.Contains(actorSnoId))
                    {
                        continue;
                    }

                    if (!_minimapItemsDic.ContainsKey(acd.Address))
                    {
                        bool ignore;
                        var  minimapItem = MapMarkerFactory.Create(acd, out ignore);
                        if (ignore)
                        {
                            _ignoredSnoIds.Add(actorSnoId);
                        }
                        else if (minimapItem != null)
                        {
                            _minimapItemsDic.Add(acd.Address, minimapItem);
                            itemsToAdd.Add(minimapItem);
                        }
                    }
                }

                UpdateUI(itemsToAdd, itemsToRemove);
            }
            catch (Exception exception)
            {
                OnUpdateException(exception);
            }
        }
Beispiel #17
0
 protected override void ShutdownAppSpecificItems()
 {
     // Shutdown the ContainerCache
     ContainerCache.Dispose();
 }
Beispiel #18
0
        public async Task Execute()
        {
            if (Configuration.LoadedConfiguration.IsProduction)
            {
                return;
            }

            /*Event marioEvent = ContainerCache.GetEventWithId(1003);
             * string path = Path.Combine(Path.GetDirectoryName(Program.LOCAL_LAST_TOPIC), "debug-event.json");
             * File.WriteAllText(path, ToJson(marioEvent));*/
            //await Context.Channel.SendMessageAsync("test <:thonk:508037103473655831>");
            //throw new Exception("test exception");

            //TestMessage testMessage = new TestMessage(Context.User);
            //await DiscordBot.SendInteractiveMessageAsync(Context.Channel, testMessage);

            //RecurringHousekeepingJob job = new RecurringHousekeepingJob();
            //await job.Execute(null);

            //TwitterManager.GetAccount("SSBUBot").Tweet("[Test]", "test tweet from debug command", "URL: https://google.com");

            //LineNewsTwitterHandler.HandleAdded(ContainerCache.GetLineNews().Last());

            /*IList<PopUpNews> newsList = ContainerCache.GetPopUpNews();
             * PopUpNews longestNews = newsList.FirstOrDefault();
             * foreach (PopUpNews news in newsList)
             * {
             *  if (news.TitleText[Language.EnglishUS].Length > longestNews.TitleText[Language.EnglishUS].Length)
             *  {
             *      longestNews = news;
             *  }
             * }
             *
             * await Context.Channel.SendMessageAsync("[Debug] tweeting " + longestNews.Id);
             *
             * PopUpNewsTwitterHandler.HandleAdded(longestNews);*/

            /*IList<LineNews> newsList = ContainerCache.GetLineNews();
             *
             * LineNews longestNews = newsList.FirstOrDefault();
             * OneLine longestLine = longestNews.OneLines[0];
             *
             * foreach (LineNews news in newsList)
             * {
             *  foreach (OneLine line in news.OneLines)
             *  {
             *      if (line.Text[Language.EnglishUS].Length > longestLine.Text[Language.EnglishUS].Length)
             *      {
             *          longestNews = news;
             *          longestLine = line;
             *      }
             *  }
             * }
             *
             * await Context.Channel.SendMessageAsync("[Debug] line news " + longestNews.Id + ", oneline " + longestLine.Id + " is " + longestLine.Text[Language.EnglishUS].Length);*/

            await Context.Channel.SendMessageAsync("[Debug] Test tweets...");

            PopUpNewsTwitterHandler.HandleAdded(ContainerCache.GetPopUpNews().FirstOrDefault());
            PresentTwitterHandler.HandleAdded(ContainerCache.GetPresents().FirstOrDefault());
            LineNewsTwitterHandler.HandleAdded(ContainerCache.GetLineNews().FirstOrDefault());
        }
Beispiel #19
0
        public async Task Execute(IJobExecutionContext context)
        {
            try
            {
                // Log that we're about to begin a check
                await DiscordBot.LoggingChannel.SendMessageAsync("**[BCAT]** Beginning check");

                // Download the latest Topic
                Topic topic = await BcatApi.GetDataTopic(Program.TITLE_ID, Program.PASSPHRASE);

                // Create the target folder name
                string targetFolder = string.Format(Program.LOCAL_OLD_DATA_DIRECTORY, DateTime.Now.ToString(Program.FOLDER_DATE_TIME_FORMAT));

                // Check if this the first run
                if (!Configuration.LoadedConfiguration.FirstRunCompleted)
                {
                    // Log that this is the first run of BCAT
                    await DiscordBot.LoggingChannel.SendMessageAsync("**[BCAT]** First run");

                    // Download all data
                    Dictionary <string, byte[]> downloadedData = await BcatCheckerUtils.DownloadAllData(topic, Program.TITLE_ID, Program.PASSPHRASE, targetFolder);

                    // Loop over all data
                    foreach (KeyValuePair <string, byte[]> pair in downloadedData)
                    {
                        // Get the FileType
                        FileType fileType = FileTypeExtensions.GetTypeFromName(pair.Key);

                        // Check if this is a container
                        if (fileType.IsContainer())
                        {
                            // Create a Container instance
                            Nintendo.SmashUltimate.Bcat.Container container = CreateSmashContainerInstance(fileType, pair.Value);

                            // Add this to the container cache
                            ContainerCache.AddFile(container, Path.GetFileName(pair.Key), pair.Value);
                        }
                        else
                        {
                            // Write this file out to the common file cache
                            File.WriteAllBytes(Path.Combine(Program.LOCAL_COMMON_CACHE_DIRECTORY, Path.GetFileName(pair.Key)), pair.Value);
                        }
                    }

                    // Save the configuration
                    Configuration.LoadedConfiguration.FirstRunCompleted = true;
                    Configuration.LoadedConfiguration.Write();

                    // Write out the topic
                    File.WriteAllBytes(Program.LOCAL_LAST_TOPIC, MessagePackSerializer.Serialize(topic));

                    // Log that we're about to begin a check
                    await DiscordBot.LoggingChannel.SendMessageAsync("**[BCAT]** First run complete");

                    return;
                }

                // Load the old Topic
                Topic oldTopic = MessagePackSerializer.Deserialize <Topic>(File.ReadAllBytes(Program.LOCAL_LAST_TOPIC));

#if DEBUG
                if (!Configuration.LoadedConfiguration.IsProduction)
                {
                    /*foreach (Bcat.Directory dir in oldTopic.Directories)
                     * {
                     *  if (dir.Name == "line_news")
                     *  {
                     *      Data dbgData = dir.Data.FirstOrDefault();
                     *      if (dbgData == null)
                     *      {
                     *          continue;
                     *      }
                     *      dir.Data.Remove(dbgData);
                     *      //dbgData.Digest = "deadbeef";
                     *  }
                     * }*/
                }
#endif

                // Get the differences
                List <KeyValuePair <DifferenceType, string> > differences = BcatCheckerUtils.GetTopicChanges(oldTopic, topic);

                // Check if there aren't any
                if (differences.Count == 0)
                {
                    // Nothing to do here
                    goto finished;
                }

                // Download all data
                Dictionary <string, byte[]> data = await BcatCheckerUtils.DownloadAllData(topic, Program.TITLE_ID, Program.PASSPHRASE, targetFolder);

                // Loop over every difference
                foreach (KeyValuePair <DifferenceType, string> pair in differences)
                {
                    // Log the difference
                    await DiscordBot.LoggingChannel.SendMessageAsync("**[BCAT]** diff: ``" + pair.Value + "`` (" + pair.Key.ToString() + ")");

                    // Get the FileType
                    FileType fileType = FileTypeExtensions.GetTypeFromName(pair.Value);

                    // Declare a variable to hold the method parameters
                    object[] parameters;

                    if (pair.Key == DifferenceType.Added)
                    {
                        // Get the raw file
                        byte[] rawFile = data[pair.Value];

                        // Check if this is a Container
                        if (fileType.IsContainer())
                        {
                            // Create a Container instance
                            Nintendo.SmashUltimate.Bcat.Container addedContainer = CreateSmashContainerInstance(fileType, data[pair.Value]);

                            // Add this to the container cache
                            ContainerCache.AddFile(addedContainer, Path.GetFileName(pair.Value), rawFile);

                            // Set the method parameters
                            parameters = new object[] { addedContainer };
                        }
                        else
                        {
                            // Write this file out to the common file cache
                            File.WriteAllBytes(Path.Combine(Program.LOCAL_COMMON_CACHE_DIRECTORY, Path.GetFileName(pair.Value)), data[pair.Value]);

                            // Set the method parameters
                            parameters = new object[] { rawFile };
                        }
                    }
                    else if (pair.Key == DifferenceType.Changed)
                    {
                        // Get the raw file
                        byte[] rawFile = data[pair.Value];

                        // Check if this is a Container
                        if (fileType.IsContainer())
                        {
                            // Create a Container instance
                            Nintendo.SmashUltimate.Bcat.Container addedContainer = CreateSmashContainerInstance(fileType, data[pair.Value]);

                            // Overwrite this to the container cache
                            Nintendo.SmashUltimate.Bcat.Container previousContainer = ContainerCache.OverwriteFile(addedContainer, Path.GetFileName(pair.Value), rawFile);

                            // Set the method parameters
                            parameters = new object[] { previousContainer, addedContainer };
                        }
                        else
                        {
                            // Construct the commoon cache path
                            string path = Path.Combine(Program.LOCAL_COMMON_CACHE_DIRECTORY, Path.GetFileName(pair.Value));

                            // Load the old file
                            byte[] previousRawFile = File.ReadAllBytes(path);

                            // Write this file out to the common file cache
                            File.WriteAllBytes(path, data[pair.Value]);

                            // Set the method parameters
                            parameters = new object[] { previousRawFile, rawFile };
                        }
                    }
                    else // Removed
                    {
                        // TODO: discord print
                        continue;
                    }

                    // Call every difference handler
                    await BcatCheckerUtils.CallDifferenceHandlers((int)fileType, pair.Key, parameters);
                }

                // Write out the Topic
                File.WriteAllBytes(Program.LOCAL_LAST_TOPIC, MessagePackSerializer.Serialize(topic));

finished:
                await DiscordBot.LoggingChannel.SendMessageAsync("**[BCAT]** Check complete");
            }
            catch (Exception exception)
            {
                // Notify the logging channel
                await DiscordUtil.HandleException(exception, $"in ``BcatCheckerJob``");
            }
        }