diff --git a/src/NadekoBot/Modules/Administration/Commands/SelfCommands.cs b/src/NadekoBot/Modules/Administration/Commands/SelfCommands.cs index 4d87c796..a7ac2f46 100644 --- a/src/NadekoBot/Modules/Administration/Commands/SelfCommands.cs +++ b/src/NadekoBot/Modules/Administration/Commands/SelfCommands.cs @@ -12,6 +12,9 @@ using Discord.WebSocket; using NadekoBot.Services; using NadekoBot.Services.Database.Models; using Microsoft.EntityFrameworkCore; +using System.Collections.Immutable; +using NadekoBot.DataStructures; +using NLog; namespace NadekoBot.Modules.Administration { @@ -25,8 +28,11 @@ namespace NadekoBot.Modules.Administration private static readonly object _locker = new object(); + private new static readonly Logger _log; + static SelfCommands() { + _log = LogManager.GetCurrentClassLogger(); using (var uow = DbHandler.UnitOfWork()) { var config = uow.BotConfig.GetOrCreate(); @@ -36,7 +42,7 @@ namespace NadekoBot.Modules.Administration var _ = Task.Run(async () => { - while(!NadekoBot.Ready) + while (!NadekoBot.Ready) await Task.Delay(1000); foreach (var cmd in NadekoBot.BotConfig.StartupCommands) @@ -113,7 +119,7 @@ namespace NadekoBot.Modules.Administration {Format.Code(GetText("channel"))}: {x.ChannelName}/{x.ChannelId} {Format.Code(GetText("command_text"))}: {x.CommandText}"; return str; - })),footer: GetText("page", page + 1)) + })), footer: GetText("page", page + 1)) .ConfigureAwait(false); } } @@ -123,7 +129,7 @@ namespace NadekoBot.Modules.Administration public async Task Wait(int miliseconds) { if (miliseconds <= 0) - return; + return; Context.Message.DeleteAfter(0); try { @@ -157,7 +163,7 @@ namespace NadekoBot.Modules.Administration } } - if(cmd == null) + if (cmd == null) await ReplyErrorLocalized("scrm_fail").ConfigureAwait(false); else await ReplyConfirmLocalized("scrm").ConfigureAwait(false); @@ -215,9 +221,9 @@ namespace NadekoBot.Modules.Administration } - public static async Task HandleDmForwarding(IUserMessage msg, List ownerChannels) + public static async Task HandleDmForwarding(IUserMessage msg, ImmutableArray> ownerChannels) { - if (_forwardDMs && ownerChannels.Any()) + if (_forwardDMs && ownerChannels.Length > 0) { var title = GetTextStatic("dm_from", NadekoBot.Localization.DefaultCultureInfo, @@ -238,26 +244,39 @@ namespace NadekoBot.Modules.Administration if (_forwardDMsToAllOwners) { - await Task.WhenAll(ownerChannels.Where(ch => ch.Recipient.Id != msg.Author.Id) - .Select(ch => ch.SendConfirmAsync(title, toSend))).ConfigureAwait(false); - } - else - { - var firstOwnerChannel = ownerChannels.First(); - if (firstOwnerChannel.Recipient.Id != msg.Author.Id) + var allOwnerChannels = await Task.WhenAll(ownerChannels + .Select(x => x.Value)) + .ConfigureAwait(false); + + foreach (var ownerCh in allOwnerChannels.Where(ch => ch.Recipient.Id != msg.Author.Id)) { try { - await firstOwnerChannel.SendConfirmAsync(title, toSend).ConfigureAwait(false); + await ownerCh.SendConfirmAsync(title, toSend).ConfigureAwait(false); } catch { - // ignored + _log.Warn("Can't contact owner with id {0}", ownerCh.Recipient.Id); + } + } + } + else + { + var firstOwnerChannel = await ownerChannels[0]; + if (firstOwnerChannel.Recipient.Id != msg.Author.Id) + { + try + { + await firstOwnerChannel.SendConfirmAsync(title, toSend).ConfigureAwait(false); + } + catch + { + // ignored + } } } } } - } [NadekoCommand, Usage, Description, Aliases] diff --git a/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs b/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs index 640422f7..26eda48a 100644 --- a/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs +++ b/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs @@ -36,10 +36,7 @@ namespace NadekoBot.Modules.Games static PlantPickCommands() { - -#if !GLOBAL_NADEKO NadekoBot.Client.MessageReceived += PotentialFlowerGeneration; -#endif generationChannels = new ConcurrentHashSet(NadekoBot.AllGuildConfigs .SelectMany(c => c.GenerateCurrencyChannelIds.Select(obj => obj.ChannelId))); } diff --git a/src/NadekoBot/Modules/Searches/Commands/Models/OverwatchApiModel.cs b/src/NadekoBot/Modules/Searches/Commands/Models/OverwatchApiModel.cs index 9d57f52a..50de3be8 100644 --- a/src/NadekoBot/Modules/Searches/Commands/Models/OverwatchApiModel.cs +++ b/src/NadekoBot/Modules/Searches/Commands/Models/OverwatchApiModel.cs @@ -8,59 +8,55 @@ namespace NadekoBot.Modules.Searches.Models { public OverwatchPlayer Player { get; set; } + public class OverwatchResponse + { + public object _request; + public OverwatchPlayer Eu { get; set; } + public OverwatchPlayer Kr { get; set; } + public OverwatchPlayer Us { get; set; } + } public class OverwatchPlayer { - public Data data { get; set; } - public class Data - { - public bool Missing { get; set; } = false; - public string username { get; set; } - public int level { get; set; } - public string avatar { get; set; } - public string levelFrame { get; set; } - public string star { get; set; } - [JsonProperty("games")] - public OverwatchGames Games { get; set; } - [JsonProperty("playtime")] - public OverwatchPlaytime Playtime { get; set; } - [JsonProperty("competitive")] - public OverwatchCompetitive Competitive { get; set; } - public class OverwatchGames - { - [JsonProperty("quick")] - public OverwatchQG Quick { get; set; } - [JsonProperty("competitive")] - public OverwatchCOMP Competitive { get; set; } - - public class OverwatchQG - { - public string wins { get; set; } - } - public class OverwatchCOMP - { - public string wins { get; set; } - public int lost { get; set; } - public string played { get; set; } - } - } - public class OverwatchCompetitive - { - public string rank { get; set; } - public string rank_img { get; set; } - } - public class OverwatchPlaytime - { - public string quick { get; set; } - public string competitive { get; set; } - } - } + public StatsField Stats { get; set; } } - //This is to strip the html from patch notes content - internal static string StripHTML(string input) + + public class StatsField { - var re = Regex.Replace(input, "<.*?>", String.Empty); - re = Regex.Replace(re, " ", $@" "); - return re; + public Stats Quickplay { get; set; } + public Stats Competitive { get; set; } + + } + + public class Stats + { + [JsonProperty("overall_stats")] + public OverallStats OverallStats { get; set; } + [JsonProperty("game_stats")] + public GameStats GameStats { get; set; } + } + + public class OverallStats + { + public int? comprank; + public int level; + public int prestige; + public string avatar; + public int wins; + public int losses; + public int games; + public string rank_image; + } + + public class GameStats + { + [JsonProperty("time_played")] + public float timePlayed; + } + + + public class Competitive + { + } } } \ No newline at end of file diff --git a/src/NadekoBot/Modules/Searches/Commands/OverwatchCommands.cs b/src/NadekoBot/Modules/Searches/Commands/OverwatchCommands.cs index 99ce317d..75c880b0 100644 --- a/src/NadekoBot/Modules/Searches/Commands/OverwatchCommands.cs +++ b/src/NadekoBot/Modules/Searches/Commands/OverwatchCommands.cs @@ -15,61 +15,67 @@ namespace NadekoBot.Modules.Searches [Group] public class OverwatchCommands : NadekoSubmodule { + public enum Region + { + Eu, + Us, + Kr + } + [NadekoCommand, Usage, Description, Aliases] - public async Task Overwatch(string region, [Remainder] string query = null) + public async Task Overwatch(Region region, [Remainder] string query = null) { if (string.IsNullOrWhiteSpace(query)) return; var battletag = query.Replace("#", "-"); await Context.Channel.TriggerTypingAsync().ConfigureAwait(false); - try + var model = (await GetProfile(region, battletag))?.Stats; + + if (model != null) { - await Context.Channel.TriggerTypingAsync().ConfigureAwait(false); - var model = await GetProfile(region, battletag); - - var rankimg = model.Competitive.rank_img; - var rank = model.Competitive.rank; - - if (string.IsNullOrWhiteSpace(rank)) + if (model.Competitive == null) { + var qp = model.Quickplay; var embed = new EmbedBuilder() - .WithAuthor(eau => eau.WithName($"{model.username}") + .WithAuthor(eau => eau.WithName(query) .WithUrl($"https://www.overbuff.com/players/pc/{battletag}") - .WithIconUrl($"{model.avatar}")) - .WithThumbnailUrl("https://cdn.discordapp.com/attachments/155726317222887425/255653487512256512/YZ4w2ey.png") - .AddField(fb => fb.WithName(GetText("level")).WithValue($"{model.level}").WithIsInline(true)) - .AddField(fb => fb.WithName(GetText("quick_wins")).WithValue($"{model.Games.Quick.wins}").WithIsInline(true)) + .WithIconUrl("https://cdn.discordapp.com/attachments/155726317222887425/255653487512256512/YZ4w2ey.png")) + .WithThumbnailUrl(qp.OverallStats.avatar) + .AddField(fb => fb.WithName(GetText("level")).WithValue((qp.OverallStats.level + (qp.OverallStats.prestige * 100)).ToString()).WithIsInline(true)) + .AddField(fb => fb.WithName(GetText("quick_wins")).WithValue(qp.OverallStats.wins.ToString()).WithIsInline(true)) .AddField(fb => fb.WithName(GetText("compet_rank")).WithValue("0").WithIsInline(true)) - .AddField(fb => fb.WithName(GetText("quick_playtime")).WithValue($"{model.Playtime.quick}").WithIsInline(true)) + .AddField(fb => fb.WithName(GetText("quick_playtime")).WithValue($"{qp.GameStats.timePlayed}hrs").WithIsInline(true)) .WithOkColor(); await Context.Channel.EmbedAsync(embed).ConfigureAwait(false); } else { + var qp = model.Quickplay; + var compet = model.Competitive; var embed = new EmbedBuilder() - .WithAuthor(eau => eau.WithName($"{model.username}") - .WithUrl($"https://www.overbuff.com/players/pc/{battletag}") - .WithIconUrl($"{model.avatar}")) - .WithThumbnailUrl(rankimg) - .AddField(fb => fb.WithName(GetText("level")).WithValue($"{model.level}").WithIsInline(true)) - .AddField(fb => fb.WithName(GetText("quick_wins")).WithValue($"{model.Games.Quick.wins}").WithIsInline(true)) - .AddField(fb => fb.WithName(GetText("compet_wins")).WithValue($"{model.Games.Competitive.wins}").WithIsInline(true)) - .AddField(fb => fb.WithName(GetText("compet_loses")).WithValue($"{model.Games.Competitive.lost}").WithIsInline(true)) - .AddField(fb => fb.WithName(GetText("compet_played")).WithValue($"{model.Games.Competitive.played}").WithIsInline(true)) - .AddField(fb => fb.WithName(GetText("compet_rank")).WithValue(rank).WithIsInline(true)) - .AddField(fb => fb.WithName(GetText("compet_playtime")).WithValue($"{model.Playtime.competitive}").WithIsInline(true)) - .AddField(fb => fb.WithName(GetText("quick_playtime")).WithValue($"{model.Playtime.quick}").WithIsInline(true)) + .WithAuthor(eau => eau.WithName(query) + .WithUrl($"https://www.overbuff.com/players/pc/{battletag}") + .WithIconUrl(compet.OverallStats.rank_image)) + .WithThumbnailUrl(compet.OverallStats.avatar) + .AddField(fb => fb.WithName(GetText("level")).WithValue((qp.OverallStats.level + (qp.OverallStats.prestige * 100)).ToString()).WithIsInline(true)) + .AddField(fb => fb.WithName(GetText("quick_wins")).WithValue(qp.OverallStats.wins.ToString()).WithIsInline(true)) + .AddField(fb => fb.WithName(GetText("compet_wins")).WithValue(compet.OverallStats.wins.ToString()).WithIsInline(true)) + .AddField(fb => fb.WithName(GetText("compet_loses")).WithValue(compet.OverallStats.losses.ToString()).WithIsInline(true)) + .AddField(fb => fb.WithName(GetText("compet_played")).WithValue(compet.OverallStats.games.ToString()).WithIsInline(true)) + .AddField(fb => fb.WithName(GetText("compet_rank")).WithValue(compet.OverallStats.comprank.ToString()).WithIsInline(true)) + .AddField(fb => fb.WithName(GetText("compet_playtime")).WithValue(compet.GameStats.timePlayed + "hrs").WithIsInline(true)) + .AddField(fb => fb.WithName(GetText("quick_playtime")).WithValue(qp.GameStats.timePlayed.ToString("F1") + "hrs").WithIsInline(true)) .WithColor(NadekoBot.OkColor); await Context.Channel.EmbedAsync(embed).ConfigureAwait(false); } } - catch + else { await ReplyErrorLocalized("ow_user_not_found").ConfigureAwait(false); } } - public async Task GetProfile(string region, string battletag) + public async Task GetProfile(Region region, string battletag) { try { @@ -78,11 +84,20 @@ namespace NadekoBot.Modules.Searches handler.ServerCertificateCustomValidationCallback = (x, y, z, e) => true; using (var http = new HttpClient(handler)) { - var url = - await http.GetStringAsync( - $"https://api.lootbox.eu/pc/{region.ToLower()}/{battletag}/profile"); - var model = JsonConvert.DeserializeObject(url); - return model.data; + http.AddFakeHeaders(); + var url = $"https://owapi.nadekobot.me/api/v3/u/{battletag}/stats"; + System.Console.WriteLine(url); + var res = await http.GetStringAsync($"https://owapi.nadekobot.me/api/v3/u/{battletag}/stats"); + var model = JsonConvert.DeserializeObject(res); + switch (region) + { + case Region.Eu: + return model.Eu; + case Region.Kr: + return model.Kr; + default: + return model.Us; + } } } } diff --git a/src/NadekoBot/Services/CommandHandler.cs b/src/NadekoBot/Services/CommandHandler.cs index 4cb83e38..12125a20 100644 --- a/src/NadekoBot/Services/CommandHandler.cs +++ b/src/NadekoBot/Services/CommandHandler.cs @@ -17,6 +17,8 @@ using NadekoBot.Modules.Games; using System.Collections.Concurrent; using System.Threading; using NadekoBot.DataStructures; +using System.Diagnostics; +using System.Collections.Immutable; namespace NadekoBot.Services { @@ -34,7 +36,7 @@ namespace NadekoBot.Services private readonly CommandService _commandService; private readonly Logger _log; - private List OwnerChannels { get; set; } = new List(); + private ImmutableArray> ownerChannels { get; set; } = new ImmutableArray>(); public event Func CommandExecuted = delegate { return Task.CompletedTask; }; @@ -84,28 +86,15 @@ namespace NadekoBot.Services var _ = Task.Run(async () => { await Task.Delay(5000).ConfigureAwait(false); - OwnerChannels = (await Task.WhenAll(_client.Guilds.SelectMany(g => g.Users) - .Where(u => NadekoBot.Credentials.OwnerIds.Contains(u.Id)) - .Distinct(new GuildUserComparer()) - .Select(async u => - { - try - { - return await u.CreateDMChannelAsync(); - } - catch - { - return null; - } - }))) - .Where(ch => ch != null) - .OrderBy(x => NadekoBot.Credentials.OwnerIds.IndexOf(x.Id)) - .ToList(); - if (!OwnerChannels.Any()) + _client.GetGuilds().SelectMany(g => g.Users); + + LoadOwnerChannels(); + + if (!ownerChannels.Any()) _log.Warn("No owner channels created! Make sure you've specified correct OwnerId in the credentials.json file."); else - _log.Info($"Created {OwnerChannels.Count} out of {NadekoBot.Credentials.OwnerIds.Length} owner message channels."); + _log.Info($"Created {ownerChannels.Length} out of {NadekoBot.Credentials.OwnerIds.Length} owner message channels."); }); _client.MessageReceived += MessageReceivedHandler; @@ -133,6 +122,36 @@ namespace NadekoBot.Services return Task.CompletedTask; } + private void LoadOwnerChannels() + { + var hs = new HashSet(NadekoBot.Credentials.OwnerIds); + var channels = new Dictionary>(); + + foreach (var s in _client.Shards) + { + if(hs.Count == 0) + break; + foreach (var g in s.Guilds) + { + if(hs.Count == 0) + break; + + foreach (var u in g.Users) + { + if(hs.Remove(u.Id)) + { + channels.Add(u.Id, new AsyncLazy(async () => await u.CreateDMChannelAsync())); + if(hs.Count == 0) + break; + } + } + } + } + + ownerChannels = channels.OrderBy(x => NadekoBot.Credentials.OwnerIds.IndexOf(x.Key)) + .Select(x => x.Value) + .ToImmutableArray(); + } private async Task TryRunCleverbot(IUserMessage usrMsg, SocketGuild guild) { if (guild == null) @@ -427,7 +446,7 @@ namespace NadekoBot.Services await usrMsg.Channel.SendMessageAsync(Help.DMHelpString).ConfigureAwait(false); - await SelfCommands.HandleDmForwarding(usrMsg, OwnerChannels).ConfigureAwait(false); + await SelfCommands.HandleDmForwarding(usrMsg, ownerChannels).ConfigureAwait(false); } } }