コード例 #1
0
ファイル: PsnScraper.cs プロジェクト: Nicba1010/discord-bot
        private static async Task AddOrUpdateThumbnailAsync(string contentId, string name, string url, CancellationToken cancellationToken)
        {
            var match = ContentIdMatcher.Match(contentId);

            if (!match.Success)
            {
                return;
            }

            var productCode = match.Groups["product_id"].Value;

            if (!ProductCodeLookup.ProductCode.IsMatch(productCode))
            {
                return;
            }

            name = string.IsNullOrEmpty(name) ? null : name;
            using (var db = new ThumbnailDb())
            {
                var savedItem = db.Thumbnail.FirstOrDefault(t => t.ProductCode == productCode);
                if (savedItem == null)
                {
                    var newItem = new Thumbnail
                    {
                        ProductCode = productCode,
                        ContentId   = contentId,
                        Name        = name,
                        Url         = url,
                        Timestamp   = DateTime.UtcNow.Ticks,
                    };
                    db.Thumbnail.Add(newItem);
                }
                else if (!string.IsNullOrEmpty(url))
                {
                    if (string.IsNullOrEmpty(savedItem.Url))
                    {
                        savedItem.Url = url;
                    }
                    if (string.IsNullOrEmpty(savedItem.Name) && !string.IsNullOrEmpty(name))
                    {
                        savedItem.Name = name;
                    }
                    if (!ScrapeStateProvider.IsFresh(savedItem.Timestamp))
                    {
                        if (savedItem.Url != url)
                        {
                            savedItem.Url           = url;
                            savedItem.EmbeddableUrl = null;
                        }
                        if (name != null && savedItem.Name != name)
                        {
                            savedItem.Name = name;
                        }
                    }
                    savedItem.ContentId = contentId;
                    savedItem.Timestamp = DateTime.UtcNow.Ticks;
                }
                await db.SaveChangesAsync(cancellationToken).ConfigureAwait(false);
            }
        }
コード例 #2
0
        public static async Task SetLastRunTimestampAsync(string locale, string containerId = null)
        {
            if (string.IsNullOrEmpty(locale))
            {
                throw new ArgumentException(nameof(locale));
            }

            var id = GetId(locale, containerId);

            using (var db = new ThumbnailDb())
            {
                var timestamp = db.State.FirstOrDefault(s => s.Locale == id);
                if (timestamp == null)
                {
                    db.State.Add(new State {
                        Locale = id, Timestamp = DateTime.UtcNow.Ticks
                    });
                }
                else
                {
                    timestamp.Timestamp = DateTime.UtcNow.Ticks;
                }
                await db.SaveChangesAsync().ConfigureAwait(false);
            }
        }
コード例 #3
0
        public static async Task OnMessageDeleted(MessageDeleteEventArgs args)
        {
            if (args.Channel.Id != Config.ThumbnailSpamId)
            {
                return;
            }

            if (string.IsNullOrEmpty(args.Message.Content))
            {
                return;
            }

            if (!args.Message.Attachments.Any())
            {
                return;
            }

            using var db = new ThumbnailDb();
            var thumb = db.Thumbnail.FirstOrDefault(i => i.ContentId == args.Message.Content);

            if (thumb?.EmbeddableUrl is string url && !string.IsNullOrEmpty(url) && args.Message.Attachments.Any(a => a.Url == url))
            {
                thumb.EmbeddableUrl = null;
                await db.SaveChangesAsync(Config.Cts.Token).ConfigureAwait(false);
            }
        }
コード例 #4
0
        public static async Task <string> GetThumbnailUrlAsync(this DiscordClient client, string productCode)
        {
            productCode = productCode.ToUpperInvariant();
            var tmdbInfo = await PsnClient.GetTitleMetaAsync(productCode, Config.Cts.Token).ConfigureAwait(false);

            if (tmdbInfo?.Icon.Url is string tmdbIconUrl)
            {
                return(tmdbIconUrl);
            }

            using (var db = new ThumbnailDb())
            {
                var thumb = await db.Thumbnail.FirstOrDefaultAsync(t => t.ProductCode == productCode).ConfigureAwait(false);

                //todo: add search task if not found
                if (thumb?.EmbeddableUrl is string embeddableUrl && !string.IsNullOrEmpty(embeddableUrl))
                {
                    return(embeddableUrl);
                }

                if (string.IsNullOrEmpty(thumb?.Url) || !ScrapeStateProvider.IsFresh(thumb.Timestamp))
                {
                    var gameTdbCoverUrl = await GameTdbScraper.GetThumbAsync(productCode).ConfigureAwait(false);

                    if (!string.IsNullOrEmpty(gameTdbCoverUrl))
                    {
                        if (thumb == null)
                        {
                            var addResult = await db.Thumbnail.AddAsync(new Thumbnail { ProductCode = productCode, Url = gameTdbCoverUrl }).ConfigureAwait(false);

                            thumb = addResult.Entity;
                        }
                        else
                        {
                            thumb.Url = gameTdbCoverUrl;
                        }
                        thumb.Timestamp = DateTime.UtcNow.Ticks;
                        await db.SaveChangesAsync().ConfigureAwait(false);
                    }
                }

                if (thumb?.Url is string url && !string.IsNullOrEmpty(url))
                {
                    var contentName = (thumb.ContentId ?? thumb.ProductCode);
                    var embed       = await GetEmbeddableUrlAsync(client, contentName, url).ConfigureAwait(false);

                    if (embed.url != null)
                    {
                        thumb.EmbeddableUrl = embed.url;
                        await db.SaveChangesAsync().ConfigureAwait(false);

                        return(embed.url);
                    }
                }
            }
            return(null);
        }
コード例 #5
0
        public static async Task <(string url, DiscordColor color)> GetThumbnailUrlWithColorAsync(DiscordClient client, string contentId, DiscordColor defaultColor, string url = null)
        {
            if (string.IsNullOrEmpty(contentId))
            {
                throw new ArgumentException("ContentID can't be empty", nameof(contentId));
            }

            contentId    = contentId.ToUpperInvariant();
            using var db = new ThumbnailDb();
            var info = await db.Thumbnail.FirstOrDefaultAsync(ti => ti.ContentId == contentId, Config.Cts.Token).ConfigureAwait(false);

            info ??= new Thumbnail {
                Url = url
            };
            if (info.Url == null)
            {
                return(null, defaultColor);
            }

            DiscordColor?analyzedColor = null;

            if (string.IsNullOrEmpty(info.EmbeddableUrl))
            {
                var em = await GetEmbeddableUrlAsync(client, contentId, info.Url).ConfigureAwait(false);

                if (em.url is string eUrl)
                {
                    info.EmbeddableUrl = eUrl;
                    if (em.image is byte[] jpg)
                    {
                        Config.Log.Trace("Getting dominant color for " + eUrl);
                        analyzedColor = ColorGetter.Analyze(jpg, defaultColor);
                        var c = analyzedColor.Value.Value;
                        if (c != defaultColor.Value)
                        {
                            info.EmbedColor = c;
                        }
                    }
                    await db.SaveChangesAsync(Config.Cts.Token).ConfigureAwait(false);
                }
            }
            if ((!info.EmbedColor.HasValue && !analyzedColor.HasValue) ||
                (info.EmbedColor.HasValue && info.EmbedColor.Value == defaultColor.Value))
            {
                var c = await GetImageColorAsync(info.EmbeddableUrl, defaultColor).ConfigureAwait(false);

                if (c.HasValue && c.Value.Value != defaultColor.Value)
                {
                    info.EmbedColor = c.Value.Value;
                    await db.SaveChangesAsync(Config.Cts.Token).ConfigureAwait(false);
                }
            }
            var color = info.EmbedColor.HasValue ? new DiscordColor(info.EmbedColor.Value) : defaultColor;

            return(info.EmbeddableUrl, color);
        }
コード例 #6
0
        public static async Task <(string url, DiscordColor color)> GetThumbnailUrlWithColorAsync(DiscordClient client, string contentId, DiscordColor defaultColor, string url = null)
        {
            if (string.IsNullOrEmpty(contentId))
            {
                throw new ArgumentException("ContentID can't be empty", nameof(contentId));
            }

            contentId = contentId.ToUpperInvariant();
            using (var db = new ThumbnailDb())
            {
                var info = await db.TitleInfo.FirstOrDefaultAsync(ti => ti.ContentId == contentId, Config.Cts.Token).ConfigureAwait(false);

                if (info == null)
                {
                    info = new TitleInfo {
                        ContentId = contentId, ThumbnailUrl = url, Timestamp = DateTime.UtcNow.Ticks
                    };
                    var thumb = await db.Thumbnail.FirstOrDefaultAsync(t => t.ContentId == contentId).ConfigureAwait(false);

                    if (thumb?.EmbeddableUrl is string eUrl &&
                        thumb.Url is string thumbUrl &&
                        thumbUrl == url)
                    {
                        info.ThumbnailEmbeddableUrl = eUrl;
                    }
                    info = db.TitleInfo.Add(info).Entity;
                    await db.SaveChangesAsync(Config.Cts.Token).ConfigureAwait(false);
                }
                if (string.IsNullOrEmpty(info.ThumbnailEmbeddableUrl))
                {
                    var em = await GetEmbeddableUrlAsync(client, contentId, info.ThumbnailUrl).ConfigureAwait(false);

                    if (em.url is string eUrl)
                    {
                        info.ThumbnailEmbeddableUrl = eUrl;
                        if (em.image is byte[] jpg)
                        {
                            info.EmbedColor = ColorGetter.Analyze(jpg, defaultColor).Value;
                        }
                        await db.SaveChangesAsync(Config.Cts.Token).ConfigureAwait(false);
                    }
                }
                if (!info.EmbedColor.HasValue)
                {
                    var c = await GetImageColorAsync(info.ThumbnailEmbeddableUrl, defaultColor).ConfigureAwait(false);

                    if (c.HasValue)
                    {
                        info.EmbedColor = c.Value.Value;
                        await db.SaveChangesAsync(Config.Cts.Token).ConfigureAwait(false);
                    }
                }
                var color = info.EmbedColor.HasValue ? new DiscordColor(info.EmbedColor.Value) : defaultColor;
                return(info.ThumbnailEmbeddableUrl, color);
            }
        }
コード例 #7
0
        public static bool IsFresh(string locale, DateTime dataTimestamp)
        {
            using var db = new ThumbnailDb();
            var timestamp = string.IsNullOrEmpty(locale) ? db.State.OrderBy(s => s.Timestamp).FirstOrDefault() : db.State.FirstOrDefault(s => s.Locale == locale);

            if (timestamp?.Timestamp is long checkDate && checkDate > 0)
            {
                return(new DateTime(checkDate, DateTimeKind.Utc) > dataTimestamp);
            }
            return(false);
        }
コード例 #8
0
        public static async Task SaveAsync(TSyscallStats syscallInfo)
        {
            if (syscallInfo == null || syscallInfo.Count == 0)
            {
                return;
            }

            if (await Limiter.WaitAsync(1000, Config.Cts.Token))
            {
                try
                {
                    using var db = new ThumbnailDb();
                    foreach (var productCodeMap in syscallInfo)
                    {
                        var product = db.Thumbnail.AsNoTracking().FirstOrDefault(t => t.ProductCode == productCodeMap.Key)
                                      ?? db.Thumbnail.Add(new Thumbnail {
                            ProductCode = productCodeMap.Key
                        }).Entity;
                        if (product.Id == 0)
                        {
                            await db.SaveChangesAsync(Config.Cts.Token).ConfigureAwait(false);
                        }

                        foreach (var moduleMap in productCodeMap.Value)
                        {
                            foreach (var func in moduleMap.Value)
                            {
                                var syscall = db.SyscallInfo.AsNoTracking().FirstOrDefault(sci => sci.Module == moduleMap.Key.ToUtf8() && sci.Function == func.ToUtf8())
                                              ?? db.SyscallInfo.Add(new SyscallInfo {
                                    Module = moduleMap.Key.ToUtf8(), Function = func.ToUtf8()
                                }).Entity;
                                if (syscall.Id == 0)
                                {
                                    await db.SaveChangesAsync(Config.Cts.Token).ConfigureAwait(false);
                                }

                                if (!db.SyscallToProductMap.Any(m => m.ProductId == product.Id && m.SyscallInfoId == syscall.Id))
                                {
                                    db.SyscallToProductMap.Add(new SyscallToProductMap {
                                        ProductId = product.Id, SyscallInfoId = syscall.Id
                                    });
                                }
                            }
                        }
                    }
                    await db.SaveChangesAsync(Config.Cts.Token).ConfigureAwait(false);
                }
                finally
                {
                    Limiter.Release();
                }
            }
        }
コード例 #9
0
        public static async Task <string> GetTitleNameAsync(string productCode, CancellationToken cancellationToken)
        {
            if (string.IsNullOrEmpty(productCode))
            {
                return(null);
            }

            productCode = productCode.ToUpperInvariant();
            using (var db = new ThumbnailDb())
            {
                var thumb = await db.Thumbnail.FirstOrDefaultAsync(
                    t => t.ProductCode == productCode,
                    cancellationToken : cancellationToken
                    ).ConfigureAwait(false);

                if (thumb?.Name is string title)
                {
                    return(title);
                }

                var meta = await PsnClient.GetTitleMetaAsync(productCode, cancellationToken).ConfigureAwait(false);

                title = meta?.Name;
                try
                {
                    if (!string.IsNullOrEmpty(title))
                    {
                        if (thumb == null)
                        {
                            thumb = (
                                await db.Thumbnail.AddAsync(new Thumbnail
                            {
                                ProductCode = productCode,
                                Name = title,
                            }, cancellationToken).ConfigureAwait(false)
                                ).Entity;
                        }
                        else
                        {
                            thumb.Name = title;
                        }
                        await db.SaveChangesAsync(cancellationToken).ConfigureAwait(false);
                    }
                }
                catch (Exception e)
                {
                    Config.Log.Warn(e);
                }

                return(title);
            }
        }
コード例 #10
0
        public static string GetTitleName(string productCode)
        {
            if (string.IsNullOrEmpty(productCode))
            {
                return(null);
            }

            using (var db = new ThumbnailDb())
            {
                var thumb = db.Thumbnail.FirstOrDefault(t => t.ProductCode == productCode.ToUpperInvariant());
                return(thumb?.Name);
            }
        }
コード例 #11
0
ファイル: Psn.cs プロジェクト: dio-gh/discord-bot
        public async Task Rescan(CommandContext ctx)
        {
            using var db = new ThumbnailDb();
            var items = db.State.ToList();

            foreach (var state in items)
            {
                state.Timestamp = 0;
            }
            await db.SaveChangesAsync(Config.Cts.Token).ConfigureAwait(false);

            await ctx.ReactWithAsync(Config.Reactions.Success, "Reset state timestamps").ConfigureAwait(false);
        }
コード例 #12
0
        public static bool IsFresh(string locale, string?containerId = null)
        {
            var id = GetId(locale, containerId);

            using var db = new ThumbnailDb();
            var timestamp = string.IsNullOrEmpty(id) ? db.State.OrderBy(s => s.Timestamp).FirstOrDefault() : db.State.FirstOrDefault(s => s.Locale == id);

            if (timestamp?.Timestamp is long checkDate && checkDate > 0)
            {
                return(IsFresh(new DateTime(checkDate, DateTimeKind.Utc)));
            }
            return(false);
        }
コード例 #13
0
ファイル: Psn.cs プロジェクト: dio-gh/discord-bot
        public static async Task SearchForGame(CommandContext ctx, string search, int maxResults)
        {
            var ch = await ctx.GetChannelForSpamAsync().ConfigureAwait(false);

            DiscordMessage msg = null;

            try
            {
                if (string.IsNullOrEmpty(search))
                {
                    var interact = ctx.Client.GetInteractivity();
                    msg = await msg.UpdateOrCreateMessageAsync(ch, "What game are you looking for?").ConfigureAwait(false);

                    var response = await interact.WaitForMessageAsync(m => m.Author == ctx.User && m.Channel == ch).ConfigureAwait(false);

                    await msg.DeleteAsync().ConfigureAwait(false);

                    msg = null;
                    if (string.IsNullOrEmpty(response.Result?.Content))
                    {
                        await ctx.ReactWithAsync(Config.Reactions.Failure).ConfigureAwait(false);

                        return;
                    }

                    search = response.Result.Content;
                }

                string titleId    = null;
                var    productIds = ProductCodeLookup.GetProductIds(search);
                if (productIds.Count > 0)
                {
                    using var db = new ThumbnailDb();
                    var contentId = await db.Thumbnail.FirstOrDefaultAsync(t => t.ProductCode == productIds[0].ToUpperInvariant()).ConfigureAwait(false);

                    if (contentId?.ContentId != null)
                    {
                        titleId = contentId.ContentId;
                    }
                    if (contentId?.Name != null)
                    {
                        search = contentId.Name;
                    }
                }

                var alteredSearch = search.Trim();
                if (alteredSearch.EndsWith("demo", StringComparison.InvariantCultureIgnoreCase))
                {
                    alteredSearch = alteredSearch[..^ 4].TrimEnd();
コード例 #14
0
        public async Task Fix(CommandContext ctx, [Description("Product ID to reset")] string productId)
        {
            var count = 0;

            using (var db = new ThumbnailDb())
            {
                var items = db.Thumbnail.Where(i => i.ProductCode == productId && !string.IsNullOrEmpty(i.EmbeddableUrl));
                foreach (var thumb in items)
                {
                    thumb.EmbeddableUrl = null;
                }
                count = await db.SaveChangesAsync(Config.Cts.Token).ConfigureAwait(false);
            }
            await ctx.RespondAsync($"Removed {count} cached links").ConfigureAwait(false);
        }
コード例 #15
0
 private static bool NeedLookup(string contentId)
 {
     using (var db = new ThumbnailDb())
         if (db.Thumbnail.FirstOrDefault(t => t.ContentId == contentId) is Thumbnail thumbnail)
         {
             if (!string.IsNullOrEmpty(thumbnail.Url))
             {
                 if (ScrapeStateProvider.IsFresh(new DateTime(thumbnail.Timestamp, DateTimeKind.Utc)))
                 {
                     return(false);
                 }
             }
         }
     return(true);
 }
コード例 #16
0
        public static async Task CleanAsync(CancellationToken cancellationToken)
        {
            using (var db = new ThumbnailDb())
            {
                var latestTimestamp = db.State.OrderByDescending(s => s.Timestamp).FirstOrDefault()?.Timestamp;
                if (!latestTimestamp.HasValue)
                {
                    return;
                }

                var cutOff   = new DateTime(latestTimestamp.Value, DateTimeKind.Utc).Add(-CheckInterval);
                var oldItems = db.State.Where(s => s.Timestamp < cutOff.Ticks);
                db.State.RemoveRange(oldItems);
                await db.SaveChangesAsync(cancellationToken).ConfigureAwait(false);
            }
        }
コード例 #17
0
ファイル: Psn.cs プロジェクト: dio-gh/discord-bot
        public async Task Rename(CommandContext ctx, [Description("Product code such as BLUS12345")] string productCode, [RemainingText, Description("New game title to save in the database")] string title)
        {
            productCode  = productCode.ToUpperInvariant();
            using var db = new ThumbnailDb();
            var item = db.Thumbnail.FirstOrDefault(t => t.ProductCode == productCode);

            if (item == null)
            {
                await ctx.ReactWithAsync(Config.Reactions.Failure, $"Unknown product code {productCode}", true).ConfigureAwait(false);
            }
            else
            {
                item.Name = title;
                await db.SaveChangesAsync().ConfigureAwait(false);

                await ctx.ReactWithAsync(Config.Reactions.Success, "Title updated successfully").ConfigureAwait(false);
            }
        }
コード例 #18
0
ファイル: Psn.cs プロジェクト: dio-gh/discord-bot
        public async Task Fix(CommandContext ctx, [Description("Product ID to reset")] string productId)
        {
            var linksToRemove = new List <(string contentId, string link)>();

            using (var db = new ThumbnailDb())
            {
                var items = db.Thumbnail.Where(i => i.ProductCode == productId && !string.IsNullOrEmpty(i.EmbeddableUrl));
                foreach (var thumb in items)
                {
                    linksToRemove.Add((thumb.ContentId, thumb.EmbeddableUrl));
                    thumb.EmbeddableUrl = null;
                }
                await db.SaveChangesAsync(Config.Cts.Token).ConfigureAwait(false);
            }
            await TryDeleteThumbnailCache(ctx, linksToRemove).ConfigureAwait(false);

            await ctx.RespondAsync($"Removed {linksToRemove.Count} cached links").ConfigureAwait(false);
        }
コード例 #19
0
ファイル: BotStats.cs プロジェクト: guipleite/discord-bot
 private void AppendSyscallsStats(DiscordEmbedBuilder embed)
 {
     using (var db = new ThumbnailDb())
     {
         var syscallCount       = db.SyscallInfo.Where(sci => sci.Function.StartsWith("sys_")).Distinct().Count();
         var syscallModuleCount = db.SyscallInfo.Where(sci => sci.Function.StartsWith("sys_")).Select(sci => sci.Module).Distinct().Count();
         var totalFuncCount     = db.SyscallInfo.Select(sci => sci.Function).Distinct().Count();
         var totalModuleCount   = db.SyscallInfo.Select(sci => sci.Module).Distinct().Count();
         var fwCallCount        = totalFuncCount - syscallCount;
         var fwModuleCount      = totalModuleCount - syscallModuleCount;
         var gameCount          = db.SyscallToProductMap.Select(m => m.ProductId).Distinct().Count();
         embed.AddField("SceCall Stats",
                        $"Tracked game IDs: {gameCount}\n" +
                        $"Tracked syscalls: {syscallCount} function{(syscallCount == 1 ? "" : "s")} in {syscallModuleCount} module{(syscallModuleCount == 1 ? "" : "s")}\n" +
                        $"Tracked fw calls: {fwCallCount} function{(fwCallCount == 1 ? "" : "s")} in {fwModuleCount} module{(fwModuleCount == 1 ? "" : "s")}\n",
                        true);
     }
 }
コード例 #20
0
        public async Task Add(CommandContext ctx, [Description("Product code such as BLUS12345")] string contentId, [RemainingText, Description("New game title to save in the database")] string title)
        {
            contentId = contentId.ToUpperInvariant();
            var productCode      = contentId;
            var productCodeMatch = ProductCodeLookup.ProductCode.Match(contentId);
            var contentIdMatch   = PsnScraper.ContentIdMatcher.Match(contentId);

            if (contentIdMatch.Success)
            {
                productCode = contentIdMatch.Groups["product_id"].Value;
            }
            else if (productCodeMatch.Success)
            {
                productCode = productCodeMatch.Groups["letters"].Value + productCodeMatch.Groups["numbers"].Value;
                contentId   = null;
            }
            else
            {
                await ctx.ReactWithAsync(Config.Reactions.Failure, "Invalid content id", true).ConfigureAwait(false);

                return;
            }

            using var db = new ThumbnailDb();
            var item = db.Thumbnail.FirstOrDefault(t => t.ProductCode == productCode);

            if (item != null)
            {
                await ctx.ReactWithAsync(Config.Reactions.Failure, $"Product code {contentId} already exists", true).ConfigureAwait(false);
            }
            else
            {
                item = new Thumbnail
                {
                    ProductCode = contentId,
                    ContentId   = contentId,
                    Name        = title,
                };
                db.Add(item);
                await db.SaveChangesAsync().ConfigureAwait(false);

                await ctx.ReactWithAsync(Config.Reactions.Success, "Title added successfully").ConfigureAwait(false);
            }
        }
コード例 #21
0
 private static void AppendSyscallsStats(DiscordEmbedBuilder embed)
 {
     try
     {
         using var db = new ThumbnailDb();
         var syscallCount   = db.SyscallInfo.AsNoTracking().Where(sci => sci.Function.StartsWith("sys_") || sci.Function.StartsWith("_sys_")).Distinct().Count();
         var totalFuncCount = db.SyscallInfo.AsNoTracking().Select(sci => sci.Function).Distinct().Count();
         var fwCallCount    = totalFuncCount - syscallCount;
         var gameCount      = db.SyscallToProductMap.AsNoTracking().Select(m => m.ProductId).Distinct().Count();
         embed.AddField("SceCall Stats",
                        $"Tracked game IDs: {gameCount}\n" +
                        $"Tracked syscalls: {syscallCount} function{(syscallCount == 1 ? "" : "s")}\n" +
                        $"Tracked fw calls: {fwCallCount} function{(fwCallCount == 1 ? "" : "s")}\n",
                        true);
     }
     catch (Exception e)
     {
         Config.Log.Warn(e);
     }
 }
コード例 #22
0
ファイル: Syscall.cs プロジェクト: guipleite/discord-bot
        private async Task ReturnSyscallsByGameAsync(CommandContext ctx, string productId)
        {
            productId = productId.ToUpperInvariant();
            string title = null;

            using (var db = new ThumbnailDb())
            {
                title = db.Thumbnail.FirstOrDefault(t => t.ProductCode == productId)?.Name;
                title = string.IsNullOrEmpty(title) ? productId : $"[{productId}] {title.Trim(40)}";
                var sysInfoList = await db.SyscallToProductMap.AsNoTracking()
                                  .Where(m => m.Product.ProductCode == productId)
                                  .Select(m => m.SyscallInfo)
                                  .Distinct()
                                  .ToAsyncEnumerable()
                                  .OrderBy(sci => sci.Module)
                                  .ThenBy(sci => sci.Function)
                                  .ToList()
                                  .ConfigureAwait(false);

                if (ctx.User.Id == 216724245957312512UL)
                {
                    sysInfoList = sysInfoList.Where(i => i.Function.StartsWith("sys_", StringComparison.InvariantCultureIgnoreCase)).ToList();
                }
                if (sysInfoList.Any())
                {
                    var result = new StringBuilder($"List of syscalls used by `{title}`:```").AppendLine();
                    foreach (var sci in sysInfoList)
                    {
                        result.AppendLine($"{sci.Module}: {sci.Function}");
                    }
                    await ctx.SendAutosplitMessageAsync(result.Append("```")).ConfigureAwait(false);
                }
                else
                {
                    await ctx.RespondAsync($"No information available for `{title}`").ConfigureAwait(false);
                }
            }
        }
コード例 #23
0
            public async Task ImportMc(CommandContext ctx)
            {
                if (await ImportLockObj.WaitAsync(0).ConfigureAwait(false))
                {
                    try
                    {
                        await CompatList.ImportMetacriticScoresAsync().ConfigureAwait(false);

                        await using var db = new ThumbnailDb();
                        var linkedItems = await db.Thumbnail.CountAsync(i => i.MetacriticId != null).ConfigureAwait(false);

                        await ctx.RespondAsync($"Importing Metacritic info was successful, linked {linkedItems} items").ConfigureAwait(false);
                    }
                    finally
                    {
                        ImportLockObj.Release();
                    }
                }
                else
                {
                    await ctx.RespondAsync("Another import operation is already in progress").ConfigureAwait(false);
                }
            }
コード例 #24
0
        private async Task ReturnSyscallsByGameAsync(CommandContext ctx, string productId)
        {
            productId    = productId.ToUpperInvariant();
            using var db = new ThumbnailDb();
            var title = db.Thumbnail.FirstOrDefault(t => t.ProductCode == productId)?.Name;

            title = string.IsNullOrEmpty(title) ? productId : $"[{productId}] {title.Trim(40)}";
            var sysInfoList = db.SyscallToProductMap.AsNoTracking()
                              .Where(m => m.Product.ProductCode == productId)
                              .Select(m => m.SyscallInfo)
                              .Distinct()
                              .AsEnumerable()
                              .OrderBy(sci => sci.Function.TrimStart('_'))
                              .ToList();

            if (sysInfoList.Any())
            {
                var result = new StringBuilder();
                foreach (var sci in sysInfoList)
                {
                    result.AppendLine(sci.Function);
                }
                using var memoryStream = Config.MemoryStreamManager.GetStream();
                using var streamWriter = new StreamWriter(memoryStream, Encoding.UTF8);
                await streamWriter.WriteAsync(result).ConfigureAwait(false);

                await streamWriter.FlushAsync().ConfigureAwait(false);

                memoryStream.Seek(0, SeekOrigin.Begin);
                await ctx.RespondWithFileAsync($"{productId} syscalls.txt", memoryStream, $"List of syscalls used by `{title}`").ConfigureAwait(false);
            }
            else
            {
                await ctx.RespondAsync($"No information available for `{title}`").ConfigureAwait(false);
            }
        }
コード例 #25
0
        public async Task Rename(CommandContext ctx, [Description("Old function name")] string oldFunctionName, [Description("New function name")] string newFunctionName)
        {
            using var db = new ThumbnailDb();
            var oldMatches = await db.SyscallInfo.Where(sci => sci.Function == oldFunctionName).ToListAsync().ConfigureAwait(false);

            if (oldMatches.Count == 0)
            {
                await ctx.RespondAsync($"Function `{oldFunctionName}` could not be found").ConfigureAwait(false);
                await Search(ctx, oldFunctionName).ConfigureAwait(false);

                return;
            }

            if (oldMatches.Count > 1)
            {
                await ctx.RespondAsync("More than one matching function was found, I can't handle this right now 😔").ConfigureAwait(false);
                await Search(ctx, oldFunctionName).ConfigureAwait(false);

                return;
            }

            var conflicts = await db.SyscallInfo.Where(sce => sce.Function == newFunctionName).AnyAsync().ConfigureAwait(false);

            if (conflicts)
            {
                await ctx.RespondAsync($"There is already a function `{newFunctionName}`").ConfigureAwait(false);
                await Search(ctx, newFunctionName).ConfigureAwait(false);

                return;
            }

            oldMatches[0].Function = newFunctionName;
            await db.SaveChangesAsync().ConfigureAwait(false);

            await ctx.RespondAsync($"Function `{oldFunctionName}` was successfully renamed to `{newFunctionName}`").ConfigureAwait(false);
        }
コード例 #26
0
        public static async Task <string> GetThumbnailUrlAsync(this DiscordClient client, string productCode)
        {
            productCode = productCode.ToUpperInvariant();
            using (var db = new ThumbnailDb())
            {
                var thumb = await db.Thumbnail.FirstOrDefaultAsync(t => t.ProductCode == productCode.ToUpperInvariant()).ConfigureAwait(false);

                //todo: add search task if not found
                if (thumb?.EmbeddableUrl is string embeddableUrl && !string.IsNullOrEmpty(embeddableUrl))
                {
                    return(embeddableUrl);
                }

                if (string.IsNullOrEmpty(thumb?.Url) || !ScrapeStateProvider.IsFresh(thumb.Timestamp))
                {
                    var gameTdbCoverUrl = await GameTdbScraper.GetThumbAsync(productCode).ConfigureAwait(false);

                    if (!string.IsNullOrEmpty(gameTdbCoverUrl))
                    {
                        if (thumb == null)
                        {
                            var addResult = await db.Thumbnail.AddAsync(new Thumbnail { ProductCode = productCode, Url = gameTdbCoverUrl }).ConfigureAwait(false);

                            thumb = addResult.Entity;
                        }
                        else
                        {
                            thumb.Url = gameTdbCoverUrl;
                        }
                        thumb.Timestamp = DateTime.UtcNow.Ticks;
                        await db.SaveChangesAsync().ConfigureAwait(false);
                    }
                }

                if (thumb?.Url is string url && !string.IsNullOrEmpty(url))
                {
                    if (!string.IsNullOrEmpty(Path.GetExtension(url)))
                    {
                        thumb.EmbeddableUrl = url;
                        await db.SaveChangesAsync().ConfigureAwait(false);

                        return(url);
                    }

                    try
                    {
                        using (var imgStream = await HttpClient.GetStreamAsync(url).ConfigureAwait(false))
                            using (var memStream = new MemoryStream())
                            {
                                await imgStream.CopyToAsync(memStream).ConfigureAwait(false);

                                // minimum jpg size is 119 bytes, png is 67 bytes
                                if (memStream.Length < 64)
                                {
                                    return(null);
                                }
                                memStream.Seek(0, SeekOrigin.Begin);
                                var spam = await client.GetChannelAsync(Config.ThumbnailSpamId).ConfigureAwait(false);

                                //var message = await spam.SendFileAsync(memStream, (thumb.ContentId ?? thumb.ProductCode) + ".jpg").ConfigureAwait(false);
                                var contentName = (thumb.ContentId ?? thumb.ProductCode);
                                var message     = await spam.SendFileAsync(contentName + ".jpg", memStream, contentName).ConfigureAwait(false);

                                thumb.EmbeddableUrl = message.Attachments.First().Url;
                                await db.SaveChangesAsync().ConfigureAwait(false);

                                return(thumb.EmbeddableUrl);
                            }
                    }
                    catch (Exception e)
                    {
                        Config.Log.Warn(e);
                    }
                }
            }
            return(null);
        }
コード例 #27
0
        internal static async Task Main(string[] args)
        {
            Config.TelemetryClient?.TrackEvent("startup");

            Console.WriteLine("Confinement: " + SandboxDetector.Detect());
            if (args.Length > 0 && args[0] == "--dry-run")
            {
                Console.WriteLine("Database path: " + Path.GetDirectoryName(Path.GetFullPath(DbImporter.GetDbPath("fake.db", Environment.SpecialFolder.ApplicationData))));
                if (Assembly.GetEntryAssembly().GetCustomAttribute <UserSecretsIdAttribute>() != null)
                {
                    Console.WriteLine("Bot config path: " + Path.GetDirectoryName(Path.GetFullPath(Config.GoogleApiConfigPath)));
                }
                return;
            }

            if (Process.GetCurrentProcess().Id == 0)
            {
                Config.Log.Info("Well, this was unexpected");
            }
            var singleInstanceCheckThread = new Thread(() =>
            {
                using var instanceLock = new Mutex(false, @"Global\RPCS3 Compatibility Bot");
                if (instanceLock.WaitOne(1000))
                {
                    try
                    {
                        InstanceCheck.Release();
                        ShutdownCheck.Wait();
                    }
                    finally
                    {
                        instanceLock.ReleaseMutex();
                    }
                }
            });

            try
            {
                singleInstanceCheckThread.Start();
                if (!await InstanceCheck.WaitAsync(1000).ConfigureAwait(false))
                {
                    Config.Log.Fatal("Another instance is already running.");
                    return;
                }

                if (string.IsNullOrEmpty(Config.Token) || Config.Token.Length < 16)
                {
                    Config.Log.Fatal("No token was specified.");
                    return;
                }

                if (SandboxDetector.Detect() == SandboxType.Docker)
                {
                    Config.Log.Info("Checking for updates...");
                    try
                    {
                        var(updated, stdout) = await Sudo.Bot.UpdateAsync().ConfigureAwait(false);

                        if (!string.IsNullOrEmpty(stdout) && updated)
                        {
                            Config.Log.Debug(stdout);
                        }
                        if (updated)
                        {
                            Sudo.Bot.Restart(InvalidChannelId, "Restarted due to new bot updates not present in this Docker image");
                            return;
                        }
                    }
                    catch (Exception e)
                    {
                        Config.Log.Error(e, "Failed to check for updates");
                    }
                }

                using (var db = new BotDb())
                    if (!await DbImporter.UpgradeAsync(db, Config.Cts.Token))
                    {
                        return;
                    }

                using (var db = new ThumbnailDb())
                    if (!await DbImporter.UpgradeAsync(db, Config.Cts.Token))
                    {
                        return;
                    }

                await SqlConfiguration.RestoreAsync().ConfigureAwait(false);

                Config.Log.Debug("Restored configuration variables from persistent storage");

                await StatsStorage.RestoreAsync().ConfigureAwait(false);

                Config.Log.Debug("Restored stats from persistent storage");

                var backgroundTasks = Task.WhenAll(
                    AmdDriverVersionProvider.RefreshAsync(),
#if !DEBUG
                    new PsnScraper().RunAsync(Config.Cts.Token),
                    GameTdbScraper.RunAsync(Config.Cts.Token),
#endif
                    StatsStorage.BackgroundSaveAsync(),
                    CompatList.ImportCompatListAsync()
                    );

                try
                {
                    if (!Directory.Exists(Config.IrdCachePath))
                    {
                        Directory.CreateDirectory(Config.IrdCachePath);
                    }
                }
                catch (Exception e)
                {
                    Config.Log.Warn(e, $"Failed to create new folder {Config.IrdCachePath}: {e.Message}");
                }

                var config = new DiscordConfiguration
                {
                    Token            = Config.Token,
                    TokenType        = TokenType.Bot,
                    MessageCacheSize = Config.MessageCacheSize,
                    LoggerFactory    = Config.LoggerFactory,
                };
                using var client = new DiscordClient(config);
                var commands = client.UseCommandsNext(new CommandsNextConfiguration
                {
                    StringPrefixes = new[] { Config.CommandPrefix, Config.AutoRemoveCommandPrefix },
                    Services       = new ServiceCollection().BuildServiceProvider(),
                });
                commands.RegisterConverter(new TextOnlyDiscordChannelConverter());
                commands.RegisterCommands <Misc>();
                commands.RegisterCommands <CompatList>();
                commands.RegisterCommands <Sudo>();
                commands.RegisterCommands <CommandsManagement>();
                commands.RegisterCommands <ContentFilters>();
                commands.RegisterCommands <Warnings>();
                commands.RegisterCommands <Explain>();
                commands.RegisterCommands <Psn>();
                commands.RegisterCommands <Invites>();
                commands.RegisterCommands <Moderation>();
                commands.RegisterCommands <Ird>();
                commands.RegisterCommands <BotMath>();
                commands.RegisterCommands <Pr>();
                commands.RegisterCommands <Events>();
                commands.RegisterCommands <E3>();
                commands.RegisterCommands <Cyberpunk2077>();
                commands.RegisterCommands <Rpcs3Ama>();
                commands.RegisterCommands <BotStats>();
                commands.RegisterCommands <Syscall>();
                commands.RegisterCommands <ForcedNicknames>();
                commands.RegisterCommands <Minesweeper>();

                if (!string.IsNullOrEmpty(Config.AzureComputerVisionKey))
                {
                    commands.RegisterCommands <Vision>();
                }

                commands.CommandErrored += UnknownCommandHandler.OnError;

                client.UseInteractivity(new InteractivityConfiguration());

                client.Ready += async(c, _) =>
                {
                    var admin = await c.GetUserAsync(Config.BotAdminId).ConfigureAwait(false);

                    Config.Log.Info("Bot is ready to serve!");
                    Config.Log.Info("");
                    Config.Log.Info($"Bot user id : {c.CurrentUser.Id} ({c.CurrentUser.Username})");
                    Config.Log.Info($"Bot admin id : {Config.BotAdminId} ({admin.Username ?? "???"}#{admin.Discriminator ?? "????"})");
                    Config.Log.Info("");
                };
                client.GuildAvailable += async(c, gaArgs) =>
                {
                    await BotStatusMonitor.RefreshAsync(c).ConfigureAwait(false);

                    Watchdog.DisconnectTimestamps.Clear();
                    Watchdog.TimeSinceLastIncomingMessage.Restart();
                    if (gaArgs.Guild.Id != Config.BotGuildId)
                    {
#if DEBUG
                        Config.Log.Warn($"Unknown discord server {gaArgs.Guild.Id} ({gaArgs.Guild.Name})");
#else
                        Config.Log.Warn($"Unknown discord server {gaArgs.Guild.Id} ({gaArgs.Guild.Name}), leaving...");
                        await gaArgs.Guild.LeaveAsync().ConfigureAwait(false);
#endif
                        return;
                    }

                    Config.Log.Info($"Server {gaArgs.Guild.Name} is available now");
                    Config.Log.Info($"Checking moderation backlogs in {gaArgs.Guild.Name}...");
                    try
                    {
                        await Task.WhenAll(
                            Starbucks.CheckBacklogAsync(c, gaArgs.Guild).ContinueWith(_ => Config.Log.Info($"Starbucks backlog checked in {gaArgs.Guild.Name}."), TaskScheduler.Default),
                            DiscordInviteFilter.CheckBacklogAsync(c, gaArgs.Guild).ContinueWith(_ => Config.Log.Info($"Discord invites backlog checked in {gaArgs.Guild.Name}."), TaskScheduler.Default)
                            ).ConfigureAwait(false);
                    }
                    catch (Exception e)
                    {
                        Config.Log.Warn(e, "Error running backlog tasks");
                    }
                    Config.Log.Info($"All moderation backlogs checked in {gaArgs.Guild.Name}.");
                };
                client.GuildAvailable   += (c, _) => UsernameValidationMonitor.MonitorAsync(c, true);
                client.GuildUnavailable += (_, guArgs) =>
                {
                    Config.Log.Warn($"{guArgs.Guild.Name} is unavailable");
                    return(Task.CompletedTask);
                };
#if !DEBUG
/*
 *              client.GuildDownloadCompleted += async gdcArgs =>
 *                                               {
 *                                                   foreach (var guild in gdcArgs.Guilds)
 *                                                       await ModProvider.SyncRolesAsync(guild.Value).ConfigureAwait(false);
 *                                               };
 */
#endif
                client.MessageReactionAdded += Starbucks.Handler;
                client.MessageReactionAdded += ContentFilterMonitor.OnReaction;

                client.MessageCreated += (_, __) => { Watchdog.TimeSinceLastIncomingMessage.Restart(); return(Task.CompletedTask); };
                client.MessageCreated += ContentFilterMonitor.OnMessageCreated; // should be first
                client.MessageCreated += GlobalMessageCache.OnMessageCreated;
                var mediaScreenshotMonitor = new MediaScreenshotMonitor(client);
                if (!string.IsNullOrEmpty(Config.AzureComputerVisionKey))
                {
                    client.MessageCreated += mediaScreenshotMonitor.OnMessageCreated;
                }
                client.MessageCreated += ProductCodeLookup.OnMessageCreated;
                client.MessageCreated += LogParsingHandler.OnMessageCreated;
                client.MessageCreated += LogAsTextMonitor.OnMessageCreated;
                client.MessageCreated += DiscordInviteFilter.OnMessageCreated;
                client.MessageCreated += PostLogHelpHandler.OnMessageCreated;
                client.MessageCreated += BotReactionsHandler.OnMessageCreated;
                client.MessageCreated += GithubLinksHandler.OnMessageCreated;
                client.MessageCreated += NewBuildsMonitor.OnMessageCreated;
                client.MessageCreated += TableFlipMonitor.OnMessageCreated;
                client.MessageCreated += IsTheGamePlayableHandler.OnMessageCreated;
                client.MessageCreated += EmpathySimulationHandler.OnMessageCreated;

                client.MessageUpdated += GlobalMessageCache.OnMessageUpdated;
                client.MessageUpdated += ContentFilterMonitor.OnMessageUpdated;
                client.MessageUpdated += DiscordInviteFilter.OnMessageUpdated;
                client.MessageUpdated += EmpathySimulationHandler.OnMessageUpdated;

                client.MessageDeleted += GlobalMessageCache.OnMessageDeleted;
                if (Config.DeletedMessagesLogChannelId > 0)
                {
                    client.MessageDeleted += DeletedMessagesMonitor.OnMessageDeleted;
                }
                client.MessageDeleted += ThumbnailCacheMonitor.OnMessageDeleted;
                client.MessageDeleted += EmpathySimulationHandler.OnMessageDeleted;

                client.MessagesBulkDeleted += GlobalMessageCache.OnMessagesBulkDeleted;

                client.UserUpdated += UsernameSpoofMonitor.OnUserUpdated;
                client.UserUpdated += UsernameZalgoMonitor.OnUserUpdated;

                client.GuildMemberAdded += Greeter.OnMemberAdded;
                client.GuildMemberAdded += UsernameSpoofMonitor.OnMemberAdded;
                client.GuildMemberAdded += UsernameZalgoMonitor.OnMemberAdded;
                client.GuildMemberAdded += UsernameValidationMonitor.OnMemberAdded;

                client.GuildMemberUpdated += UsernameSpoofMonitor.OnMemberUpdated;
                client.GuildMemberUpdated += UsernameZalgoMonitor.OnMemberUpdated;
                client.GuildMemberUpdated += UsernameValidationMonitor.OnMemberUpdated;

                Watchdog.DisconnectTimestamps.Enqueue(DateTime.UtcNow);

                try
                {
                    await client.ConnectAsync().ConfigureAwait(false);
                }
                catch (Exception e)
                {
                    Config.Log.Error(e, "Failed to connect to Discord: " + e.Message);
                    throw;
                }

                ulong? channelId  = null;
                string restartMsg = null;
                using (var db = new BotDb())
                {
                    var chState = db.BotState.FirstOrDefault(k => k.Key == "bot-restart-channel");
                    if (chState != null)
                    {
                        if (ulong.TryParse(chState.Value, out var ch))
                        {
                            channelId = ch;
                        }
                        db.BotState.Remove(chState);
                    }
                    var msgState = db.BotState.FirstOrDefault(i => i.Key == "bot-restart-msg");
                    if (msgState != null)
                    {
                        restartMsg = msgState.Value;
                        db.BotState.Remove(msgState);
                    }
                    db.SaveChanges();
                }
                if (string.IsNullOrEmpty(restartMsg))
                {
                    restartMsg = null;
                }

                if (channelId.HasValue)
                {
                    Config.Log.Info($"Found channelId {channelId}");
                    DiscordChannel channel;
                    if (channelId == InvalidChannelId)
                    {
                        channel = await client.GetChannelAsync(Config.ThumbnailSpamId).ConfigureAwait(false);

                        await channel.SendMessageAsync(restartMsg ?? "Bot has suffered some catastrophic failure and was restarted").ConfigureAwait(false);
                    }
                    else
                    {
                        channel = await client.GetChannelAsync(channelId.Value).ConfigureAwait(false);

                        await channel.SendMessageAsync("Bot is up and running").ConfigureAwait(false);
                    }
                }
                else
                {
                    Config.Log.Debug($"Args count: {args.Length}");
                    var pArgs = args.Select(a => a == Config.Token ? "<Token>" : $"[{a}]");
                    Config.Log.Debug("Args: " + string.Join(" ", pArgs));
                }

                Config.Log.Debug("Running RPCS3 update check thread");
                backgroundTasks = Task.WhenAll(
                    backgroundTasks,
                    NewBuildsMonitor.MonitorAsync(client),
                    Watchdog.Watch(client),
                    InviteWhitelistProvider.CleanupAsync(client),
                    UsernameValidationMonitor.MonitorAsync(client),
                    Psn.Check.MonitorFwUpdates(client, Config.Cts.Token),
                    Watchdog.SendMetrics(client),
                    Watchdog.CheckGCStats(),
                    mediaScreenshotMonitor.ProcessWorkQueue()
                    );

                while (!Config.Cts.IsCancellationRequested)
                {
                    if (client.Ping > 1000)
                    {
                        Config.Log.Warn($"High ping detected: {client.Ping}");
                    }
                    await Task.Delay(TimeSpan.FromMinutes(1), Config.Cts.Token).ContinueWith(dt => { /* in case it was cancelled */ }, TaskScheduler.Default).ConfigureAwait(false);
                }
                await backgroundTasks.ConfigureAwait(false);
            }
            catch (Exception e)
            {
                if (!Config.inMemorySettings.ContainsKey("shutdown"))
                {
                    Config.Log.Fatal(e, "Experienced catastrophic failure, attempting to restart...");
                }
            }
            finally
            {
                Config.TelemetryClient?.Flush();
                ShutdownCheck.Release();
                if (singleInstanceCheckThread.IsAlive)
                {
                    singleInstanceCheckThread.Join(100);
                }
            }
            if (!Config.inMemorySettings.ContainsKey("shutdown"))
            {
                Sudo.Bot.Restart(InvalidChannelId, null);
            }
        }
コード例 #28
0
        public static async Task <(int funcs, int links)> FixInvalidFunctionNamesAsync()
        {
            var syscallStats = new TSyscallStats();
            int funcs = 0, links = 0;

            using (var db = new ThumbnailDb())
            {
                var funcsToFix = new List <SyscallInfo>(0);
                try
                {
                    funcsToFix = await db.SyscallInfo.Where(sci => sci.Function.Contains('(')).ToListAsync().ConfigureAwait(false);

                    funcs = funcsToFix.Count;
                    if (funcs == 0)
                    {
                        return(0, 0);
                    }

                    foreach (var sci in funcsToFix)
                    {
                        var productIds = await db.SyscallToProductMap.AsNoTracking().Where(m => m.SyscallInfoId == sci.Id).Select(m => m.Product.ProductCode).Distinct().ToListAsync().ConfigureAwait(false);

                        links += productIds.Count;
                        foreach (var productId in productIds)
                        {
                            if (!syscallStats.TryGetValue(productId, out var scInfo))
                            {
                                syscallStats[productId] = scInfo = new Dictionary <string, HashSet <string> >();
                            }
                            if (!scInfo.TryGetValue(sci.Module, out var smInfo))
                            {
                                scInfo[sci.Module] = smInfo = new HashSet <string>();
                            }
                            smInfo.Add(sci.Function.Split('(', 2)[0]);
                        }
                    }
                }
                catch (Exception e)
                {
                    Config.Log.Warn(e, "Failed to build fixed syscall mappings");
                    throw e;
                }
                await SaveAsync(syscallStats).ConfigureAwait(false);

                if (await Limiter.WaitAsync(1000, Config.Cts.Token))
                {
                    try
                    {
                        db.SyscallInfo.RemoveRange(funcsToFix);
                        await db.SaveChangesAsync().ConfigureAwait(false);
                    }
                    catch (Exception e)
                    {
                        Config.Log.Warn(e, "Failed to remove broken syscall mappings");
                        throw e;
                    }
                    finally
                    {
                        Limiter.Release();
                    }
                }
            }
            return(funcs, links);
        }
コード例 #29
0
        public static async Task <(int funcs, int links)> FixDuplicatesAsync()
        {
            int funcs = 0, links = 0;

            using (var db = new ThumbnailDb())
            {
                var duplicateFunctionNames = await db.SyscallInfo.Where(sci => db.SyscallInfo.Count(isci => isci.Function == sci.Function && isci.Module == sci.Module) > 1).Distinct().ToListAsync().ConfigureAwait(false);

                if (duplicateFunctionNames.Count == 0)
                {
                    return(0, 0);
                }

                if (await Limiter.WaitAsync(1000, Config.Cts.Token))
                {
                    try
                    {
                        foreach (var dupFunc in duplicateFunctionNames)
                        {
                            var dups = db.SyscallInfo.Where(sci => sci.Function == dupFunc.Function && sci.Module == dupFunc.Module).ToList();
                            if (dups.Count < 2)
                            {
                                continue;
                            }

                            var mostCommonDup = dups.Select(dup => (dup, count: db.SyscallToProductMap.Count(scm => scm.SyscallInfoId == dup.Id))).OrderByDescending(stat => stat.count).First().dup;
                            var dupsToRemove  = dups.Where(df => df.Id != mostCommonDup.Id).ToList();
                            funcs += dupsToRemove.Count;
                            foreach (var dupToRemove in dupsToRemove)
                            {
                                var mappings = db.SyscallToProductMap.Where(scm => scm.SyscallInfoId == dupToRemove.Id).ToList();
                                links += mappings.Count;
                                foreach (var mapping in mappings)
                                {
                                    if (!db.SyscallToProductMap.Any(scm => scm.ProductId == mapping.ProductId && scm.SyscallInfoId == mostCommonDup.Id))
                                    {
                                        db.SyscallToProductMap.Add(new SyscallToProductMap {
                                            ProductId = mapping.ProductId, SyscallInfoId = mostCommonDup.Id
                                        });
                                    }
                                }
                            }
                            await db.SaveChangesAsync().ConfigureAwait(false);

                            db.SyscallInfo.RemoveRange(dupsToRemove);
                            await db.SaveChangesAsync().ConfigureAwait(false);
                        }
                        await db.SaveChangesAsync().ConfigureAwait(false);
                    }
                    catch (Exception e)
                    {
                        Config.Log.Warn(e, "Failed to remove duplicate syscall entries");
                        throw;
                    }
                    finally
                    {
                        Limiter.Release();
                    }
                }
            }
            return(funcs, links);
        }
コード例 #30
0
        public async Task Search(CommandContext ctx, [RemainingText, Description("Product ID, module, or function name. **Case sensitive**")] string search)
        {
            if (string.IsNullOrEmpty(search))
            {
                await ctx.ReactWithAsync(Config.Reactions.Failure, "No meaningful search query provided").ConfigureAwait(false);

                return;
            }

            var productCodes = ProductCodeLookup.GetProductIds(search);

            if (productCodes.Any())
            {
                await ReturnSyscallsByGameAsync(ctx, productCodes.First()).ConfigureAwait(false);

                return;
            }

            if (ctx.User.Id == 216724245957312512UL && !search.StartsWith("sys_", StringComparison.InvariantCultureIgnoreCase))
            {
                await ctx.RespondAsync($"This is not a _syscall_, {ctx.User.Mention}").ConfigureAwait(false);

                return;
            }

            using var db = new ThumbnailDb();
            if (db.SyscallInfo.Any(sci => sci.Module == search || sci.Function == search))
            {
                var productInfoList = db.SyscallToProductMap.AsNoTracking()
                                      .Where(m => m.SyscallInfo.Module == search || m.SyscallInfo.Function == search)
                                      .Select(m => new { m.Product.ProductCode, Name = m.Product.Name.StripMarks() ?? "???" })
                                      .Distinct()
                                      .ToList();
                var groupedList = productInfoList
                                  .GroupBy(m => m.Name, m => m.ProductCode, StringComparer.InvariantCultureIgnoreCase)
                                  .OrderBy(g => g.Key, StringComparer.OrdinalIgnoreCase)
                                  .ToList();
                if (groupedList.Any())
                {
                    var bigList = groupedList.Count >= Config.MaxSyscallResultLines;

                    var result   = new StringBuilder();
                    var fullList = bigList ? new StringBuilder() : null;
                    result.AppendLine($"List of games using `{search}`:```");
                    var c = 0;
                    foreach (var gi in groupedList)
                    {
                        var productIds = string.Join(", ", gi.Distinct().OrderBy(pc => pc).AsEnumerable());
                        if (c < Config.MaxSyscallResultLines)
                        {
                            result.AppendLine($"{gi.Key.Trim(60)} [{productIds}]");
                        }
                        if (bigList)
                        {
                            fullList.AppendLine($"{gi.Key} [{productIds}]");
                        }
                        c++;
                    }
                    await ctx.SendAutosplitMessageAsync(result.Append("```")).ConfigureAwait(false);

                    if (bigList)
                    {
                        using var memoryStream = new MemoryStream((int)(fullList.Capacity * 1.25));
                        using var streamWriter = new StreamWriter(memoryStream, Encoding.UTF8);
                        await streamWriter.WriteAsync(fullList).ConfigureAwait(false);

                        await streamWriter.FlushAsync().ConfigureAwait(false);

                        memoryStream.Seek(0, SeekOrigin.Begin);
                        await ctx.RespondWithFileAsync($"{search}.txt", memoryStream, $"See attached file for full list of {groupedList.Count} entries").ConfigureAwait(false);
                    }
                }
                else
                {
                    await ctx.RespondAsync($"No games found that use `{search}`").ConfigureAwait(false);
                }
            }
            else
            {
                var result  = new StringBuilder("Unknown entity name");
                var modules = await db.SyscallInfo.Select(sci => sci.Module).Distinct().ToListAsync().ConfigureAwait(false);

                var substrModules = modules.Where(m => m.Contains(search, StringComparison.CurrentCultureIgnoreCase));
                var fuzzyModules  = modules
                                    .Select(m => (name: m, score: search.GetFuzzyCoefficientCached(m)))
                                    .Where(i => i.score > 0.6)
                                    .OrderByDescending(i => i.score)
                                    .Select(i => i.name)
                                    .ToList();
                modules = substrModules
                          .Concat(fuzzyModules)
                          .Distinct()
                          .OrderBy(m => m, StringComparer.OrdinalIgnoreCase)
                          .ToList();
                var modulesFound = modules.Any();
                if (modulesFound)
                {
                    result.AppendLine(", possible modules:```");
                    foreach (var m in modules)
                    {
                        result.AppendLine(m);
                    }
                    result.AppendLine("```");
                }

                var functions = await db.SyscallInfo.Select(sci => sci.Function).Distinct().ToListAsync().ConfigureAwait(false);

                var substrFuncs = functions.Where(f => f.Contains(search, StringComparison.InvariantCultureIgnoreCase));
                var fuzzyFuncs  = functions
                                  .Select(f => (name: f, score: search.GetFuzzyCoefficientCached(f)))
                                  .Where(i => i.score > 0.6)
                                  .OrderByDescending(i => i.score)
                                  .Select(i => i.name)
                                  .ToList();
                functions = substrFuncs
                            .Concat(fuzzyFuncs)
                            .Distinct()
                            .OrderBy(f => f, StringComparer.OrdinalIgnoreCase)
                            .ToList();
                var functionsFound = functions.Any();
                if (functionsFound)
                {
                    if (modulesFound)
                    {
                        result.AppendLine("Possible functions:```");
                    }
                    else
                    {
                        result.AppendLine(", possible functions:```");
                    }
                    foreach (var f in functions)
                    {
                        result.AppendLine(f);
                    }
                    result.AppendLine("```");
                }
                await ctx.SendAutosplitMessageAsync(result).ConfigureAwait(false);
            }
        }