public async Task SetFeaturePermissionForChannel(CommandContext ctx, [Description("The feature to configure")] string feature, [Description("The channel to configure (without the #)")] string channel, [Description("The default permission on the feature")] bool permission) { await ctx.TriggerTypingAsync(); if (!feature.IsFeature()) { var emoji = DiscordEmoji.FromName(ctx.Client, ":no_entry:"); var embed = new DiscordEmbedBuilder { Title = "Invalid Feature", Description = $"{emoji} There is no feature called `{feature}`. Check all the features with `admin features`.", Color = DiscordColor.Red }; await ctx.RespondAsync("", embed : embed); return; } var configuration = GuildConfiguration.FromGuild(ctx.Guild); if (!configuration.PermissionsByFeatureChannel.TryGetValue(feature, out var permissions)) { permissions = new Dictionary <string, bool>(); configuration.PermissionsByFeatureChannel[feature] = permissions; } permissions[channel] = permission; configuration.Save(); await ctx.RespondAsync($"The default permission to the `{feature}` feature on the `{channel}` channel is now `{permission}`."); }
public async Task SetFeaturePermission(CommandContext ctx, [Description("The feature to configure")] string feature, [Description("The default permission on the feature")] bool permission) { await ctx.TriggerTypingAsync(); if (!feature.IsFeature()) { var emoji = DiscordEmoji.FromName(ctx.Client, ":no_entry:"); var embed = new DiscordEmbedBuilder { Title = "Invalid Feature", Description = $"{emoji} There is no feature called `{feature}`. Check all the features with `admin features`.", Color = DiscordColor.Red }; await ctx.RespondAsync("", embed : embed); return; } var configuration = GuildConfiguration.FromGuild(ctx.Guild); configuration.PermissionByFeature[feature] = permission; configuration.Save(); await ctx.RespondAsync($"The default permission to the `{feature}` feature is now `{permission}`."); }
public async Task ClearFeaturePermissions(CommandContext ctx, [Description("The feature to clear permissions")] string feature) { await ctx.TriggerTypingAsync(); if (!feature.IsFeature()) { var emoji = DiscordEmoji.FromName(ctx.Client, ":no_entry:"); var embed = new DiscordEmbedBuilder { Title = "Invalid Feature", Description = $"{emoji} There is no feature called `{feature}`. Check all the features with `admin ListFeatures`.", Color = DiscordColor.Red }; await ctx.RespondAsync("", embed : embed); return; } var configuration = GuildConfiguration.FromGuild(ctx.Guild); configuration.PermissionByFeature.Remove(feature); configuration.PermissionsByFeatureChannel.Remove(feature); configuration.PermissionsByFeatureRole.Remove(feature); configuration.PermissionsByFeatureRoleChannel.Remove(feature); configuration.Save(); await ctx.RespondAsync($"All the explicit permissions to the `{feature}` feature were cleared."); }
public async Task Coin(CommandContext ctx) { if (!await CanExecute(ctx, Features.Games)) { return; } Log.Debug($"Requesting {nameof(Coin)}()..."); var cfg = GuildConfiguration.FromGuild(ctx.Guild); var isHead = Rand.Next(0, 2) == 0; var embed = new DiscordEmbedBuilder { Title = isHead ? "Head!" : "Tail!", Description = $"{ctx.User.Mention}, your coin flipped {(isHead ? "**Head**" : "**Tail**")}!", Color = DiscordColor.Goldenrod, Url = BasicExtensions.SiteUrl(), ImageUrl = BasicExtensions.SiteUrl() + $"/images/coin-{(isHead ? "head" : "tail")}.png", Author = new DiscordEmbedBuilder.EmbedAuthor { Name = "WoTClans", Url = BasicExtensions.SiteUrl() }, Footer = new DiscordEmbedBuilder.EmbedFooter { Text = $"Flipped at {DateTime.UtcNow:yyyy-MM-dd HH:mm:ss} UTC." } }; Log.Debug($"Returned {nameof(Coin)}() = {isHead}"); await ctx.RespondAsync("", embed : embed); }
public async Task TestPermissions(CommandContext ctx, [Description("The feature")] string feature, [Description("The role")] string role, [Description("The channel")] string channel) { await ctx.TriggerTypingAsync(); if (!feature.IsFeature()) { var emoji = DiscordEmoji.FromName(ctx.Client, ":no_entry:"); var embed = new DiscordEmbedBuilder { Title = "Invalid Feature", Description = $"{emoji} There is no feature called `{feature}`. Check all the features with `admin ListFeatures`.", Color = DiscordColor.Red }; await ctx.RespondAsync("", embed : embed); return; } var configuration = GuildConfiguration.FromGuild(ctx.Guild); var result = configuration.CanCallerExecute(feature, new[] { role }, channel, out var reason); await ctx.RespondAsync($"The feature `{feature}` is **{(result ? "allowed" : "denied")}** on role `{role}` in the channel `#{channel}`. Explanation: {reason}"); }
public async Task SetPlataform(CommandContext ctx, [Description("Plataform")] string plataform) { if (!Enum.TryParse(plataform, true, out Plataform plat)) { var emoji = DiscordEmoji.FromName(ctx.Client, ":no_entry:"); var embed = new DiscordEmbedBuilder { Title = "Wrong parameter", Description = $"{emoji} The plataform should be `{Plataform.XBOX}` or `{Plataform.PS}`.", Color = DiscordColor.Red }; await ctx.RespondAsync("", embed : embed); return; } await ctx.TriggerTypingAsync(); var configuration = GuildConfiguration.FromGuild(ctx.Guild); configuration.Plataform = plat; configuration.Name = ctx.Guild.Name; configuration.Region = ctx.Guild.RegionId; configuration.Save(); await ctx.RespondAsync($"The plataform is now {plataform}."); }
private static Task DiscordOnGuildAvailable(GuildCreateEventArgs args) { Log.Info($"Guild Available: {args.Guild.Name}.{args.Guild.RegionId}.{args.Guild.Id} with {args.Guild.MemberCount} members."); var config = GuildConfiguration.FromGuild(args.Guild); config.Save(); return(Task.CompletedTask); }
public async Task SetWhoIAm(CommandContext ctx, [Description("The *gamer tag* or *PSN Name*")][RemainingText] string gamerTag = "") { try { await ctx.TriggerTypingAsync(); if (!await CanExecute(ctx, Features.Players)) { return; } long userId = (long)(ctx?.User?.Id ?? 0UL); if (userId == 0) { await ctx.RespondAsync($"Sorry, {ctx.User.Mention}. I don't now your Discord User Id! WTF!??!"); return; } var cfg = GuildConfiguration.FromGuild(ctx.Guild); var plataform = GetPlataform(gamerTag, cfg.Plataform, out gamerTag); if (string.IsNullOrWhiteSpace(gamerTag)) { gamerTag = ctx.User.Username; } var provider = new DbProvider(_connectionString); var playerId = provider.GetPlayerIdByName(plataform, gamerTag); if (playerId == null) { await ctx.RespondAsync($"Sorry, {ctx.User.Mention}, I do not track `{gamerTag}` yet. Are you a member of a tracked clan? Are you sure about the gamer tag?"); return; } var recorder = new DbRecorder(_connectionString); recorder.AssociateDiscordUserToPlayer(userId, playerId.Value); await ctx.RespondAsync($"Ok, {ctx.User.Mention}, for now on you can use `me` instead of your full {plataform.TagName()} on " + $"commands that take it as a parameters. I promisse to never abuse this association, and protected it from use outside of this system. " + $"If you want me to remove this piece of information use the `ForgetWhoIAm` command."); return; } catch (Exception ex) { Log.Error($"{nameof(WhoIAm)}()", ex); await ctx.RespondAsync($"Sorry, {ctx.User.Mention}. There was an error... the *Coder* will be notified of `{ex.Message}`."); return; } }
public async Task SetSilentDeny(CommandContext ctx, [Description("If `true` the denies will be silent.")] bool useSilentDeny) { await ctx.TriggerTypingAsync(); var configuration = GuildConfiguration.FromGuild(ctx.Guild); configuration.SilentDeny = useSilentDeny; configuration.Save(); await ctx.RespondAsync($"The Silend Deny is now `{useSilentDeny}`."); }
public async Task ClanDelete(CommandContext ctx, [Description("The clan Tag")] string clanTag) { await ctx.TriggerTypingAsync(); var userId = ctx.User?.Id ?? 0; Log.Info($"Requesting {nameof(ClanDelete)}({clanTag}) by {userId}..."); if (userId != _coder) { var emoji = DiscordEmoji.FromName(ctx.Client, ":no_entry:"); var embed = new DiscordEmbedBuilder { Title = "Access denied", Description = $"{emoji} You may be a *coder*, but you are not **The Coder**!", Color = DiscordColor.Red }; await ctx.RespondAsync("", embed : embed); return; } var cfg = GuildConfiguration.FromGuild(ctx.Guild); var platform = GetPlatform(clanTag, cfg.Plataform, out clanTag); clanTag = clanTag.Trim('[', ']'); clanTag = clanTag.ToUpperInvariant(); if (!ClanTagRegex.IsMatch(clanTag)) { await ctx.RespondAsync($"You must send a **valid** clan **tag** as parameter, {ctx.User?.Mention}."); return; } try { var putter = new Putter(platform, ConfigurationManager.AppSettings["ApiAdminKey"]); putter.DeleteClan(clanTag); await ctx.RespondAsync( $"The clan `{clanTag}` on `{platform}` was deleted from the site."); Log.Debug($"{nameof(ClanDelete)} returned ok."); } catch (Exception ex) { Log.Error($"{nameof(ClanDelete)}", ex); await ctx.RespondAsync( $"Sorry, {ctx.User?.Mention}. There was an error... the *Coder* will be notified of `{ex.Message}`."); } }
public async Task SetDefaultPermission(CommandContext ctx, [Description("The default permission")] bool permission) { await ctx.TriggerTypingAsync(); var configuration = GuildConfiguration.FromGuild(ctx.Guild); configuration.PermissionDefault = permission; configuration.Save(); await ctx.RespondAsync($"The default permission to every feature on every channel and role is now `{permission}`."); }
public async Task ResetPermissions(CommandContext ctx) { await ctx.TriggerTypingAsync(); var cfg = GuildConfiguration.FromGuild(ctx.Guild); cfg.PermissionByFeature.Clear(); cfg.PermissionsByFeatureChannel.Clear(); cfg.PermissionsByFeatureRole.Clear(); cfg.PermissionsByFeatureRoleChannel.Clear(); cfg.PermissionDefault = true; cfg.Save(); await ctx.RespondAsync($"All permissions were reset. Any command can be called by anyone everywhere."); }
public async Task Site(CommandContext ctx) { if (!await CanExecute(ctx, Features.General)) { return; } var config = GuildConfiguration.FromGuild(ctx.Guild); var embed = new DiscordEmbedBuilder { Title = "WoT Clans", Description = $"The best WoT site in the universe is just a click away from you, {ctx.User.Mention}!", Color = DiscordColor.DarkGreen, Url = $"https://{(config.Plataform == Plataform.PS ? "ps." : "")}wotclans.com.br" }; await ctx.RespondAsync("", embed : embed); }
public async Task Site(CommandContext ctx) { if (!await CanExecute(ctx, Features.General)) { return; } Log.Debug($"Requesting {nameof(Site)}()..."); var config = GuildConfiguration.FromGuild(ctx.Guild); var embed = new DiscordEmbedBuilder { Title = "WoT Clans", Description = $"The best WoT site in the universe is just a click away from you, {ctx.User.Mention}!", Color = DiscordColor.DarkGreen, Url = "https://wotclans.com.br" }; await ctx.RespondAsync("", embed : embed); }
protected async Task <bool> CanExecute(CommandContext ctx, string feature) { var cfg = GuildConfiguration.FromGuild(ctx.Guild); if (!cfg.CanCallerExecute(feature, ctx.Member, ctx.Channel, out var reason)) { if (!cfg.SilentDeny) { var emoji = DiscordEmoji.FromName(ctx.Client, ":no_entry:"); var embed = new DiscordEmbedBuilder { Title = "Access denied", Description = $"{emoji} {reason}", Color = DiscordColor.Red }; await ctx.RespondAsync("", embed : embed); } return(await Task.Run(() => false)); } return(await Task.Run(() => true)); }
public async Task ClanTopOnTank(CommandContext ctx, [Description("The clan **tag**")] string clanTag, [Description("The Tank name, as it appears in battles. If it has spaces, enclose it on quotes.")][RemainingText] string tankName) { if (!await CanExecute(ctx, Features.Clans)) { return; } await ctx.TriggerTypingAsync(); if (string.IsNullOrWhiteSpace(clanTag)) { await ctx.RespondAsync($"You must send a clan tag as parameter, {ctx.User.Mention}."); return; } Log.Debug($"Requesting {nameof(ClanTopOnTank)}({clanTag}, {tankName})..."); var cfg = GuildConfiguration.FromGuild(ctx.Guild); var platform = GetPlatform(clanTag, cfg.Plataform, out clanTag); if (!ClanTagRegex.IsMatch(clanTag)) { await ctx.RespondAsync($"You must send a **valid** clan **tag** as parameter, {ctx.User.Mention}."); return; } var provider = new DbProvider(_connectionString); var clan = provider.GetClan(platform, clanTag); if (clan == null) { platform = platform == Platform.PS ? Platform.XBOX : Platform.PS; clan = provider.GetClan(platform, clanTag); if (clan == null) { await ctx.RespondAsync( $"Can't find on a clan with tag `[{clanTag}]`, {ctx.User.Mention}. Maybe my site doesn't track it yet... or you have the wrong clan tag."); return; } } if (!clan.Enabled) { await ctx.RespondAsync( $"Data collection for the `[{clan.ClanTag}]` is disabled, {ctx.User.Mention}. Maybe the clan went too small, or inactive."); return; } var tankCommands = new TankCommands(); var tank = tankCommands.FindTank(platform, tankName, out _); if (tank == null) { await ctx.RespondAsync($"Can't find a tank with `{tankName}` on the name, {ctx.User.Mention}."); return; } var tr = provider.GetTanksReferences(tank.Plataform, null, tank.TankId, false, false, false).FirstOrDefault(); if (tr == null) { await ctx.RespondAsync($"Sorry, there is no tank statistics for the `{tank.Name}`, {ctx.User.Mention}."); return; } if (tr.Tier < 5) { await ctx.RespondAsync($"Sorry, this command is meant to be used only with tanks Tier 5 and above, {ctx.User.Mention}."); return; } var players = provider.GetClanPlayerIdsOnTank(platform, clan.ClanId, tr.TankId).ToList(); if (players.Count <= 0) { await ctx.RespondAsync($"No players from the `[{clan.ClanTag}]` has battles on the `{tank.Name}`, {ctx.User.Mention}, as far as the database is up to date."); return; } var waitMsg = await ctx.RespondAsync($"Please wait as data for {players.Count} tankers is being retrieved, {ctx.User.Mention}..."); var playerCommands = new PlayerCommands(); var fullPlayers = new ConcurrentBag <Player>(); var tasks = players.Select(async p => { var player = await playerCommands.GetPlayer(ctx, ((p.Plataform == Platform.XBOX) ? "x." : "ps.") + p.Name); if (player == null) { await ctx.RespondAsync($"Sorry, could not get updated information for player `{p.Name}`, {ctx.User.Mention}."); return; } fullPlayers.Add(player); }); await Task.WhenAll(tasks); await waitMsg.DeleteAsync(); var sb = new StringBuilder(); var maxNameLength = fullPlayers.Max(p => p.Name.Length); //sb.AppendLine($"Here `[{clan.ClanTag}]` top players on the `{tank.Name}`, {ctx.User.Mention}:"); //sb.AppendLine(); sb.AppendLine("```"); sb.AppendLine($"{platform.TagName().PadRight(maxNameLength)} {"Days".PadLeft(5)} {"Battles".PadLeft(7)} {"WN8".PadLeft(6)}"); foreach (var p in fullPlayers.OrderByDescending(p => p.Performance.All[tank.TankId].Wn8).Take(25)) { var tp = p.Performance.All[tank.TankId]; sb.AppendLine($"{(p.Name ?? string.Empty).PadRight(maxNameLength)} {(DateTime.UtcNow - tp.LastBattle).TotalDays.ToString("N0").PadLeft(5)} {tp.Battles.ToString("N0").PadLeft(7)} {tp.Wn8.ToString("N0").PadLeft(6)}"); } sb.AppendLine("```"); sb.AppendLine(); sb.AppendLine("This command is a **Premium** feature on the bot. For now it's free to use this command, but be advised that on the near future access will be restricted to Premium subscribers."); var color = clan.Top15Wn8.ToColor(); var platformPrefix = clan.Plataform == Platform.PS ? "ps." : string.Empty; var embed = new DiscordEmbedBuilder { Title = $"`{clan.ClanTag}` top players on the `{tank.Name}`", Description = sb.ToString(), Color = new DiscordColor(color.R, color.G, color.B), ThumbnailUrl = tank.SmallImageUrl, Url = $"https://{platformPrefix}wotclans.com.br/Clan/{clan.ClanTag}", Author = new DiscordEmbedBuilder.EmbedAuthor { Name = "WoTClans", Url = $"https://{platformPrefix}wotclans.com.br" }, Footer = new DiscordEmbedBuilder.EmbedFooter { Text = $"Calculated at {DateTime.UtcNow:yyyy-MM-dd HH:mm} UTC" } }; await ctx.RespondAsync("", embed : embed); }
public async Task Clan(CommandContext ctx, [Description("The clan **tag**")] string clanTag) { if (!await CanExecute(ctx, Features.Clans)) { return; } await ctx.TriggerTypingAsync(); if (string.IsNullOrWhiteSpace(clanTag)) { await ctx.RespondAsync($"You must send a clan tag as parameter, {ctx.User.Mention}."); return; } Log.Debug($"Requesting {nameof(Clan)}({clanTag})..."); var cfg = GuildConfiguration.FromGuild(ctx.Guild); var plataform = GetPlataform(clanTag, cfg.Plataform, out clanTag); clanTag = clanTag.Trim('[', ']'); clanTag = clanTag.ToUpperInvariant(); if (!ClanTagRegex.IsMatch(clanTag)) { await ctx.RespondAsync($"You must send a **valid** clan **tag** as parameter, {ctx.User.Mention}."); return; } var provider = new DbProvider(_connectionString); var clan = provider.GetClan(plataform, clanTag); if (clan == null) { if (plataform == Plataform.PS) { plataform = Plataform.XBOX; } else { plataform = Plataform.PS; } clan = provider.GetClan(plataform, clanTag); if (clan == null) { await ctx.RespondAsync($"Can't find on a clan with tag `[{clanTag}]`, {ctx.User.Mention}. Maybe my site doesn't track it yet... or you have the wrong clan tag."); return; } } var sb = new StringBuilder(); sb.Append($"Information about the `{clan.ClanTag}`"); if (!string.IsNullOrWhiteSpace(clan.Country)) { sb.Append($" ({clan.Country.ToUpperInvariant()})"); } sb.AppendLine($", on the {clan.Plataform}, {ctx.User.Mention}:"); if (!clan.Enabled) { sb.AppendLine("Data collection for this clan is **disabled**!"); } sb.AppendLine(); sb.AppendLine($"Active Members: {clan.Active}; Total Members: {clan.Count};"); sb.AppendLine($"Recent Win Rate: {clan.ActiveWinRate:P1}; Overall Win Rate: {clan.TotalWinRate:P1};"); sb.AppendLine($"Recent WN8t15: {clan.Top15Wn8:N0}; Overall WN8: {clan.TotalWn8:N0};"); if (clan.DeltaDayTop15Wn8.HasValue) { sb.AppendLine($"WN8t15 Variation from 1 day ago: {clan.DeltaDayTop15Wn8.Value:N0}"); } if (clan.DeltaWeekTop15Wn8.HasValue) { sb.AppendLine($"WN8t15 Variation from 1 week ago: {clan.DeltaWeekTop15Wn8.Value:N0}"); } if (clan.DeltaMonthTop15Wn8.HasValue) { sb.AppendLine($"WN8t15 Variation from 1 month ago: {clan.DeltaMonthTop15Wn8.Value:N0}"); } sb.AppendLine($"Recent Actives Battles: {clan.ActiveBattles:N0}; Recent Avg Tier: {clan.ActiveAvgTier:N1}"); sb.AppendLine(); sb.AppendLine("**Top 15 Active Players**"); foreach (var p in clan.Top15Players) { sb.AppendLine($"{Formatter.MaskedUrl(p.Name, new Uri(p.PlayerOverallUrl))}, {p.MonthBattles} battles, WN8: {p.MonthWn8:N0}"); } string title; if (clan.ClanTag.EqualsCiAi(clan.Name)) { title = clan.ClanTag; } else { title = $"{clan.ClanTag} - {clan.Name}"; } var color = clan.Top15Wn8.ToColor(); var plataformPrefix = clan.Plataform == Plataform.PS ? "ps." : string.Empty; var embed = new DiscordEmbedBuilder { Title = title, Description = sb.ToString(), Color = new DiscordColor(color.R, color.G, color.B), Url = $"https://{plataformPrefix}wotclans.com.br/Clan/{clan.ClanTag}", Author = new DiscordEmbedBuilder.EmbedAuthor { Name = "WoTClans", Url = $"https://{plataform}wotclans.com.br" }, Footer = new DiscordEmbedBuilder.EmbedFooter { Text = $"Data calculatet at {clan.Moment:yyyy-MM-dd HH:mm} UTC." } }; Log.Debug($"Returned {nameof(Clan)}({clan.Plataform}.{clan.ClanTag})"); await ctx.RespondAsync("", embed : embed); }
public async Task ListPermissions(CommandContext ctx) { await ctx.TriggerTypingAsync(); var cfg = GuildConfiguration.FromGuild(ctx.Guild); var sb = new StringBuilder(); sb.AppendLine("These are the permissions on this server, on the order they are evaluated:"); sb.AppendLine(); sb.AppendLine("1) Explicit permissions on a *feature* for a given *role* **and** *channel*:"); if (!cfg.PermissionsByFeatureRoleChannel.Any()) { sb.AppendLine(" No explicit permissions."); } else { foreach (var byFeature in cfg.PermissionsByFeatureRoleChannel.OrderBy(kv => kv.Key)) { foreach (var byRole in byFeature.Value.OrderBy(kv => kv.Key)) { foreach (var byChannel in byRole.Value.OrderBy(kv => kv.Key)) { sb.AppendLine($" Feature `{byFeature.Key}`, role `{byRole.Key}`, channel `#{byChannel.Key}`: {(byChannel.Value ? "allow" : "deny")}"); } } } } sb.AppendLine(); sb.AppendLine("2) Explicit permissions on a *feature* for a given *role*:"); if (!cfg.PermissionsByFeatureRole.Any()) { sb.AppendLine(" No explicit permissions."); } else { foreach (var byFeature in cfg.PermissionsByFeatureRole.OrderBy(kv => kv.Key)) { foreach (var byRole in byFeature.Value.OrderBy(kv => kv.Key)) { sb.AppendLine($" Feature `{byFeature.Key}`, role `{byRole.Key}`: {(byRole.Value ? "allow" : "deny")}"); } } } sb.AppendLine(); sb.AppendLine("3) Explicit permissions on a *feature* for a given *channel*:"); if (!cfg.PermissionsByFeatureChannel.Any()) { sb.AppendLine(" No explicit permissions."); } else { foreach (var byFeature in cfg.PermissionsByFeatureChannel.OrderBy(kv => kv.Key)) { foreach (var byChannel in byFeature.Value.OrderBy(kv => kv.Key)) { sb.AppendLine($" Feature `{byFeature.Key}`, channel `#{byChannel.Key}`: {(byChannel.Value ? "allow" : "deny")}"); } } } sb.AppendLine(); sb.AppendLine("4) Explicit permissions on a *feature*:"); if (!cfg.PermissionByFeature.Any()) { sb.AppendLine(" No explicit permissions."); } else { foreach (var byFeature in cfg.PermissionByFeature.OrderBy(kv => kv.Key)) { sb.AppendLine($" Feature `{byFeature.Key}`: {(byFeature.Value ? "allow" : "deny")}"); } } sb.AppendLine(); sb.AppendLine($"5) The *Global Permission*: {(cfg.PermissionDefault ? "allow" : "deny")}"); sb.AppendLine(); await ctx.RespondAsync(sb.ToString()); }
public async Task SetClan(CommandContext ctx, [Description("The clan tag")] string clanTag, [Description("The clan flag")] string flagCode = null, [Description("Enable or Disable the Clan")] bool enable = true, [Description("To ban or not a clan from the site")] bool isBan = false) { await ctx.TriggerTypingAsync(); var userId = ctx?.User?.Id ?? 0; Log.Info($"Requesting {nameof(SetClan)} by {userId}..."); if (userId != _coder) { var emoji = DiscordEmoji.FromName(ctx.Client, ":no_entry:"); var embed = new DiscordEmbedBuilder { Title = "Access denied", Description = $"{emoji} You may be a *coder*, but you are not **The Coder**!", Color = DiscordColor.Red }; await ctx.RespondAsync("", embed : embed); return; } var cfg = GuildConfiguration.FromGuild(ctx.Guild); var plataform = GetPlataform(clanTag, cfg.Plataform, out clanTag); clanTag = clanTag.Trim('[', ']'); clanTag = clanTag.ToUpperInvariant(); if (!ClanTagRegex.IsMatch(clanTag)) { await ctx.RespondAsync($"You must send a **valid** clan **tag** as parameter, {ctx.User.Mention}."); return; } if (!string.IsNullOrWhiteSpace(flagCode)) { flagCode = flagCode.RemoveDiacritics().ToUpperInvariant(); if (flagCode.Length != 2) { await ctx.RespondAsync($"The flag code must be 2 letters only, {ctx.User.Mention}."); return; } } Log.Warn($"{nameof(SetClan)}({clanTag}, {plataform}, {flagCode}, {enable}, {isBan})..."); try { await ctx.TriggerTypingAsync(); var provider = new DbProvider(_connectionString); var recorder = new DbRecorder(_connectionString); var cacheDirectory = ConfigurationManager.AppSettings["CacheDir"] ?? Path.GetTempPath(); var webCacheAge = TimeSpan.FromHours(4); var appId = ConfigurationManager.AppSettings["WgAppId"] ?? "demo"; var fetcher = new Fetcher(cacheDirectory) { ApplicationId = appId, WebCacheAge = webCacheAge, WebFetchInterval = TimeSpan.FromSeconds(1) }; var clan = provider.GetClan(plataform, clanTag); if (clan == null && enable) { // Check to add... await ctx.RespondAsync($"Not found `{clanTag}` on the database. Searching the WG API..."); await ctx.TriggerTypingAsync(); var clanOnSite = fetcher.FindClan(plataform, clanTag, true); if (clanOnSite == null) { await ctx.RespondAsync($"Not found `{clanTag}` on the WG API for `{plataform}`. Check the clan tag."); return; } if (clanOnSite.AllMembersCount < 7) { await ctx.RespondAsync($"The clan `{clanTag}` on `{plataform}` has only {clanOnSite.AllMembersCount}, and will not be added to the system."); return; } clanOnSite.Country = flagCode; recorder.Add(clanOnSite); await ctx.RespondAsync($"The clan `{clanTag}` on `{plataform}` with {clanOnSite.AllMembersCount} members was added to the system and " + $"should appear on the site in ~12 hours. Keep playing to achieve at least 7 members with 21 recent battles and appear on the default view."); Log.Info($"Added {plataform}.{clanTag}"); return; } if (!clan.Enabled && enable) { // Can be enabled? var clanOnSite = fetcher.GetClans(new[] { clan }).FirstOrDefault(); if (clanOnSite == null) { await ctx.RespondAsync($"Not found `{clanTag}` on the WG API for `{plataform}`. Check the clan tag."); return; } if (clanOnSite.IsDisbanded) { await ctx.RespondAsync($"The clan `{clanTag}` on `{plataform}` was disbanded."); return; } if (clanOnSite.Count < 7) { await ctx.RespondAsync($"The clan `{clanTag}` on `{plataform}` has only {clanOnSite.Count} members and will not be enabled."); return; } if (clan.DisabledReason == DisabledReason.Banned) { await ctx.RespondAsync($"The clan `{clanTag}` ({clan.ClanId}) on `{plataform}` was **banned** from the site."); return; } recorder.EnableClan(clanOnSite.Plataform, clanOnSite.ClanId); await ctx.RespondAsync($"The clan `{clanTag}` on `{plataform}` disabled for `{clan.DisabledReason}` is enabled again."); Log.Info($"Enabled {plataform}.{clanTag}"); } else if (clan.Enabled && !enable) { if (isBan) { recorder.DisableClan(clan.Plataform, clan.ClanId, DisabledReason.Banned); await ctx.RespondAsync($"The clan `{clanTag}` ({clan.ClanId}) on `{plataform}` was **BANNED** from the site."); Log.Warn($"BANNED {plataform}.{clanTag}"); } else { recorder.DisableClan(clan.Plataform, clan.ClanId, DisabledReason.Unknow); await ctx.RespondAsync($"The clan `{clanTag}` ({clan.ClanId}) on `{plataform}` was **disabled** from the site."); Log.Warn($"Disabled {plataform}.{clanTag}"); } } // change flag? flagCode = flagCode ?? string.Empty; if (flagCode.ToUpperInvariant() != (clan.Country ?? string.Empty).ToUpperInvariant()) { recorder.SetClanFlag(clan.Plataform, clan.ClanId, flagCode); await ctx.RespondAsync($"The flag of the clan `{clanTag}` on `{plataform}` was changed to `{flagCode}`."); Log.Info($"Flag changed on {plataform}.{clanTag} to {flagCode}."); } await ctx.RespondAsync($"all done for `{clan.ClanTag}` on `{plataform}`."); } catch (Exception ex) { Log.Error($"{nameof(SetClan)}", ex); await ctx.RespondAsync($"Sorry, {ctx.User.Mention}. There was an error... the *Coder* will be notified of `{ex.Message}`."); return; } }
public async Task Moe(CommandContext ctx, [Description("The Tank name, as it appears in battles. If it has spaces, enclose it on quotes.")][RemainingText] string tankName) { if (!await CanExecute(ctx, Features.Tanks)) { return; } Log.Info($"Requesting {nameof(Moe)}({tankName})"); await ctx.TriggerTypingAsync(); var cfg = GuildConfiguration.FromGuild(ctx.Guild); var tank = FindTank(cfg.Plataform, tankName, out var exact); if (tank == null) { await ctx.RespondAsync($"Can't find a tank with `{tankName}` on the name, {ctx.User.Mention}."); return; } var provider = new DbProvider(_connectionString); var moe = provider.GetMoe(tank.Plataform, null, tank.TankId).FirstOrDefault(); if (moe == null) { await ctx.RespondAsync($"Sorry, there is no MoE information for the `{tank.Name}`, {ctx.User.Mention}."); return; } var sb = new StringBuilder(); sb.AppendLine($"Here the MoE information about the `{moe.Name}`, Tier {moe.Tier.ToRomanNumeral()}, {moe.Nation.GetDescription()}, {(moe.IsPremium ? "Premium" : "Regular")}, {ctx.User.Mention}:"); sb.AppendLine($"``` * 1 mark: {moe.Moe1Dmg:N0} Total Damage"); sb.AppendLine($" ** 2 marks: {moe.Moe2Dmg:N0} Total Damage"); sb.AppendLine($"*** 3 marks: {moe.Moe3Dmg:N0} Total Damage```"); var emoji = DiscordEmoji.FromName(ctx.Client, ":exclamation:"); if (!exact) { sb.AppendLine(); sb.AppendLine($"{emoji} If this is *not the tank* you are looking for, try sending the exact short name, the one that appears during battles, or enclosing the name in quotes."); } var platformPrefix = moe.Plataform == Platform.PS ? "ps." : string.Empty; var embed = new DiscordEmbedBuilder { Title = $"{tank.Name} MoE", Description = sb.ToString(), Color = DiscordColor.Gray, ThumbnailUrl = tank.SmallImageUrl, Url = $"https://{platformPrefix}wotclans.com.br/Tanks/{tank.TankId}", Author = new DiscordEmbedBuilder.EmbedAuthor { Name = "WoTClans", Url = $"https://{platformPrefix}wotclans.com.br" }, Footer = new DiscordEmbedBuilder.EmbedFooter { Text = $"Calculated at {moe.Date:yyyy-MM-dd} from {moe.NumberOfBattles:N0} battles." } }; await ctx.RespondAsync("", embed : embed); }
public async Task RandTank(CommandContext ctx, [Description("The minimum tier of the tanks")] int minTier = 5, [Description("The maximum tier of the tanks")] int maxTier = 10, [Description("Nation of the tank, or *any*. Multiple values can be sent using *;* as separators")] string nationFilter = "any", [Description("Type of the tank, or *any*. Multiple values can be sent using *;* as separators")] string typeFilter = "any", [Description("*Premium*, *regular*, or *any*")] string premiumFilter = "any", [Description("The *gamer tag* or *PSN Name*, so it only returns tanks that the player have (or had)")] string gamerTag = null, [Description("If *true* then a tank that the given player **hadn't** will be picked")] bool notOnPlayer = false) { // Yeah... it's a Player feature but it uses more calculations for tanks... if (!await CanExecute(ctx, Features.Tanks)) { return; } if (minTier < 1) { await ctx.RespondAsync("The minimum tier is 1."); return; } if (minTier > 10) { await ctx.RespondAsync("The maximum tier is 1."); return; } if (maxTier < 1) { await ctx.RespondAsync("The minimum tier is 1."); return; } if (maxTier > 10) { await ctx.RespondAsync("The maximum tier is 1."); return; } if (maxTier < minTier) { var temp = maxTier; maxTier = minTier; minTier = temp; } await ctx.TriggerTypingAsync(); var cfg = GuildConfiguration.FromGuild(ctx.Guild); Log.Debug($"Requesting {nameof(RandTank)}({minTier}, {maxTier}, {nationFilter}, {typeFilter}, {premiumFilter}, {gamerTag ?? string.Empty}, {notOnPlayer})..."); try { var provider = new DbProvider(_connectionString); var allTanks = provider.EnumTanks(cfg.Plataform).Where(t => (t.Tier >= minTier) && (t.Tier <= maxTier)).ToList(); if (!string.IsNullOrWhiteSpace(nationFilter) && !nationFilter.EqualsCiAi("any")) { var filtersText = nationFilter.Split(new[] { ',', ';', '-', ' ' }, StringSplitOptions.RemoveEmptyEntries); var filters = new HashSet <Nation>(); foreach (var filterText in filtersText) { if (Enum.TryParse <Nation>(filterText, true, out var nation)) { filters.Add(nation); } else { await ctx.RespondAsync( $"Sorry, {ctx.User.Mention}, the nation `{filterText}` is not a valid nation. Valid nations are: {string.Join(", ", NationExtensions.GetGameNations().Select(n => $"`{n.ToString().ToLowerInvariant()}`"))}."); return; } } allTanks = allTanks.Where(t => filters.Contains(t.Nation)).ToList(); } if (!string.IsNullOrWhiteSpace(typeFilter) && !typeFilter.EqualsCiAi("any")) { var filtersText = typeFilter.Split(new[] { ',', ';', '-', ' ' }, StringSplitOptions.RemoveEmptyEntries); var filters = new HashSet <TankType>(); foreach (var filterText in filtersText) { if (Enum.TryParse <TankType>(filterText, true, out var tankType)) { filters.Add(tankType); } else { await ctx.RespondAsync( $"Sorry, {ctx.User.Mention}, the tank type `{filterText}` is not a valid type. Valid tank types are: {string.Join(", ", TankTypeExtensions.GetGameTankTypes().Select(n => $"`{n.ToString().ToLowerInvariant()}`"))}."); return; } } allTanks = allTanks.Where(t => filters.Contains(t.Type)).ToList(); } if (!string.IsNullOrWhiteSpace(premiumFilter) && !premiumFilter.EqualsCiAi("any")) { if (premiumFilter.EqualsCiAi("regular")) { allTanks = allTanks.Where(t => !t.IsPremium).ToList(); } else if (premiumFilter.EqualsCiAi("premium")) { allTanks = allTanks.Where(t => t.IsPremium).ToList(); } else { await ctx.RespondAsync( $"Sorry, {ctx.User.Mention}, the premium filter `{premiumFilter}` is not a valid value. Valid values are: `Regular`, `Premium` or `Any`."); return; } } if (!string.IsNullOrWhiteSpace(gamerTag)) { var playerCommands = new PlayerCommands(); var player = await playerCommands.GetPlayer(ctx, gamerTag); if (player == null) { Log.Debug($"Could not find player {gamerTag}"); await ctx.RespondAsync("I could not find a tanker " + $"with the Gamer Tag `{gamerTag}` on the Database, {ctx.User.Mention}. I may not track this player, or the Gamer Tag is wrong."); return; } gamerTag = player.Name; var playerTanks = player.Performance.All.Select(kv => kv.Key).ToHashSet(); allTanks = notOnPlayer ? allTanks.Where(t => !playerTanks.Contains(t.TankId)).ToList() : allTanks.Where(t => playerTanks.Contains(t.TankId)).ToList(); } if (allTanks.Count <= 0) { await ctx.RespondAsync( $"Sorry, {ctx.User.Mention}, there are no tanks with the given criteria."); return; } var number = Rand.Next(0, allTanks.Count); var tank = allTanks[number]; var platformPrefix = tank.Plataform == Platform.PS ? "ps." : string.Empty; var sb = new StringBuilder(); sb.AppendLine($"The random tank is the `{tank.Name}`, Tier {tank.Tier.ToRomanNumeral()}, {tank.Nation.GetDescription()}, {(tank.IsPremium ? "Premium" : "Regular")}, {ctx.User.Mention}!"); var embed = new DiscordEmbedBuilder { Title = $"{tank.Name} is the random Tank", Description = sb.ToString(), Color = DiscordColor.Gray, ThumbnailUrl = tank.SmallImageUrl, Url = $"https://{platformPrefix}wotclans.com.br/Tanks/{tank.TankId}", Author = new DiscordEmbedBuilder.EmbedAuthor { Name = "WoTClans", Url = $"https://{platformPrefix}wotclans.com.br" }, Footer = new DiscordEmbedBuilder.EmbedFooter { Text = $"Picked at {DateTime.UtcNow:yyyy-MM-dd HH:mm}." } }; await ctx.RespondAsync("", embed : embed); } catch (Exception ex) { Log.Error($"Error on {nameof(RandTank)}({minTier}, {maxTier}, {nationFilter}, {typeFilter}, {premiumFilter}, {gamerTag ?? string.Empty}, {notOnPlayer})", ex); await ctx.RespondAsync($"Sorry, {ctx.User.Mention}. There was an error... the *Coder* will be notified of `{ex.Message}`."); } }
/// <summary> /// Retrieves a player from database, or only WG API /// </summary> /// <param name="gamerTag"></param> /// <returns></returns> public async Task <Player> GetPlayer(CommandContext ctx, string gamerTag) { try { var cfg = GuildConfiguration.FromGuild(ctx.Guild); var plataform = GetPlataform(gamerTag, cfg.Plataform, out gamerTag); var provider = new DbProvider(_connectionString); var recorder = new DbRecorder(_connectionString); DiscordMessage willTryApiMessage = null; long?playerId = null; if (gamerTag.EqualsCiAi("me")) { playerId = provider.GetPlayerIdByDiscordId((long)ctx.User.Id); } playerId = playerId ?? provider.GetPlayerIdByName(plataform, gamerTag); if (playerId == null) { Log.Debug($"Could not find player {plataform}.{gamerTag} on the database... trying the API..."); willTryApiMessage = await ctx.RespondAsync($"I could not find a player on `{plataform}` " + $"with the Gamer Tag `{gamerTag}` on the Database, {ctx.User.Mention}. I will try the Wargaming API... it may take some time..."); } var cacheDirectory = ConfigurationManager.AppSettings["CacheDir"] ?? Path.GetTempPath(); var webCacheAge = TimeSpan.FromHours(4); var appId = ConfigurationManager.AppSettings["WgAppId"] ?? "demo"; var fetcher = new Fetcher(cacheDirectory) { ApplicationId = appId, WebCacheAge = webCacheAge, WebFetchInterval = TimeSpan.FromSeconds(1) }; Player apiPlayer = null; if (playerId == null) { await ctx.TriggerTypingAsync(); apiPlayer = fetcher.GetPlayerByGamerTag(plataform, gamerTag); if (apiPlayer == null) { Log.Debug($"Could not find player {plataform}.{gamerTag} on the WG API."); if (willTryApiMessage != null) { await willTryApiMessage.DeleteAsync("Information updated."); } await ctx.RespondAsync($"Sorry, {ctx.User.Mention}. I could not find a player on `{plataform}` " + $"with the Gamer Tag `{gamerTag}` on the Wargaming API. Are you sure about the **exact** gamer tag?."); return(null); } playerId = apiPlayer.Id; } var player = provider.GetPlayer(playerId.Value, true); var wn8Expected = provider.GetWn8ExpectedValues(player?.Plataform ?? apiPlayer.Plataform); if ((player == null) && (apiPlayer != null)) { // Not on my database, but on the API Let's work with overall data! IEnumerable <WgApi.TankPlayer> tanks = fetcher.GetTanksForPlayer(apiPlayer.Plataform, apiPlayer.Id); foreach (var t in tanks) { t.All.TreesCut = t.TreesCut; t.All.BattleLifeTimeSeconds = t.BattleLifeTimeSeconds; t.All.MarkOfMastery = t.MarkOfMastery; t.All.MaxFrags = t.MaxFrags; t.All.LastBattle = t.LastBattle; } apiPlayer.Performance = new Tanks.TankPlayerPeriods() { All = tanks.ToDictionary(t => t.TankId, t => t.All) }; apiPlayer.Calculate(wn8Expected); if (willTryApiMessage != null) { await willTryApiMessage.DeleteAsync("Information updated."); } return(apiPlayer); } if (player.Age.TotalHours > 4) { willTryApiMessage = await ctx.RespondAsync($"Data for `{player.Name}` on `{player.Plataform}` " + $"is more than {player.Age.TotalHours:N0}h old, {ctx.User.Mention}. Retrieving fresh data, please wait..."); var tanks = fetcher.GetTanksForPlayer(player.Plataform, player.Id); var allTanks = provider.GetTanks(player.Plataform).ToDictionary(t => t.TankId); var validTanks = tanks.Where(t => allTanks.ContainsKey(t.TankId)).ToArray(); recorder.Set(validTanks); var played = provider.GetWn8RawStatsForPlayer(player.Plataform, player.Id); player.Performance = played; player.Calculate(wn8Expected); player.Moment = DateTime.UtcNow; player.Origin = PlayerDataOrigin.Self; var previous = provider.GetPlayer(player.Id, player.Date, true); if (previous != null) { if (player.Check(previous, true)) { Log.Warn($"Player {player.Name}.{player.Id}@{player.Plataform} was patched."); } } if (player.CanSave()) { recorder.Set(player); if (!player.IsPatched) { var putter = new Putter(player.Plataform, ConfigurationManager.AppSettings["ApiAdminKey"]); putter.Put(player); } } else { Log.Warn($"Player {player.Name}.{player.Id}@{player.Plataform} has to much zero data."); } } else { player.Calculate(wn8Expected); } if (willTryApiMessage != null) { await willTryApiMessage.DeleteAsync("Information updated."); } return(player); } catch (Exception ex) { Log.Error($"{nameof(GetPlayer)}({gamerTag})", ex); await ctx.RespondAsync($"Sorry, {ctx.User.Mention}. There was an error... the *Coder* will be notified of `{ex.Message}`."); return(null); } }
public async Task Clan(CommandContext ctx, [Description("The clan **tag**")] string clanTag, [Description("Put `true` to dump all members of the clan")] bool all = false) { if (!await CanExecute(ctx, Features.Clans)) { return; } await ctx.TriggerTypingAsync(); if (string.IsNullOrWhiteSpace(clanTag)) { await ctx.RespondAsync($"You must send a clan tag as parameter, {ctx.User.Mention}."); return; } Log.Debug($"Requesting {nameof(Clan)}({clanTag}, {all})..."); var cfg = GuildConfiguration.FromGuild(ctx.Guild); var platform = GetPlatform(clanTag, cfg.Plataform, out clanTag); clanTag = clanTag.Trim('[', ']'); clanTag = clanTag.ToUpperInvariant(); if (!ClanTagRegex.IsMatch(clanTag)) { await ctx.RespondAsync($"You must send a **valid** clan **tag** as parameter, {ctx.User.Mention}."); return; } var provider = new DbProvider(_connectionString); var clan = provider.GetClan(platform, clanTag); if (clan == null) { platform = platform == Platform.PS ? Platform.XBOX : Platform.PS; clan = provider.GetClan(platform, clanTag); if (clan == null) { await ctx.RespondAsync( $"Can't find on a clan with tag `[{clanTag}]`, {ctx.User.Mention}. Maybe my site doesn't track it yet... or you have the wrong clan tag."); return; } } var sb = new StringBuilder(); sb.Append($"Information about the `{clan.ClanTag}`"); if (!string.IsNullOrWhiteSpace(clan.Country)) { sb.Append($" ({clan.Country.ToUpperInvariant()})"); } sb.AppendLine($", on the {clan.Plataform}, {ctx.User.Mention}:"); if (!clan.Enabled) { sb.AppendLine($"Data collection for this clan is **disabled**, the reason for this is {clan.DisabledReason}."); } sb.AppendLine(); sb.AppendLine($"Active Members: {clan.Active}; Total Members: {clan.Count};"); sb.AppendLine($"Recent Win Rate: {clan.ActiveWinRate:P1}; Overall Win Rate: {clan.TotalWinRate:P1};"); sb.AppendLine($"Recent WN8t15: {clan.Top15Wn8:N0}; Overall WN8: {clan.TotalWn8:N0};"); if (clan.DeltaDayTop15Wn8.HasValue) { sb.AppendLine($"WN8t15 Variation from 1 day ago: {clan.DeltaDayTop15Wn8.Value:N0}"); } if (clan.DeltaWeekTop15Wn8.HasValue) { sb.AppendLine($"WN8t15 Variation from 1 week ago: {clan.DeltaWeekTop15Wn8.Value:N0}"); } if (clan.DeltaMonthTop15Wn8.HasValue) { sb.AppendLine($"WN8t15 Variation from 1 month ago: {clan.DeltaMonthTop15Wn8.Value:N0}"); } sb.AppendLine($"Recent Actives Battles: {clan.ActiveBattles:N0}; Recent Avg Tier: {clan.ActiveAvgTier:N1}"); sb.AppendLine(); sb.AppendLine("**Top 15 Active Players**"); foreach (var p in clan.Top15Players) { sb.AppendLine( $"{Formatter.MaskedUrl(p.Name, new Uri(p.PlayerOverallUrl))}, {p.MonthBattles} battles, WN8: {p.MonthWn8:N0}"); } var title = clan.ClanTag.EqualsCiAi(clan.Name) ? clan.ClanTag : $"{clan.ClanTag} - {clan.Name}"; var color = clan.Top15Wn8.ToColor(); var platformPrefix = clan.Plataform == Platform.PS ? "ps." : string.Empty; var embed = new DiscordEmbedBuilder { Title = title, Description = sb.ToString(), Color = new DiscordColor(color.R, color.G, color.B), Url = $"https://{platformPrefix}wotclans.com.br/Clan/{clan.ClanTag}", Author = new DiscordEmbedBuilder.EmbedAuthor { Name = "WoTClans", Url = $"https://{platform}wotclans.com.br" }, Footer = new DiscordEmbedBuilder.EmbedFooter { Text = $"Data calculated at {clan.Moment:yyyy-MM-dd HH:mm} UTC." } }; Log.Debug($"Returned {nameof(Clan)}({clan.Plataform}.{clan.ClanTag})"); await ctx.RespondAsync("", embed : embed); if (all) { // We need more responses, as discord limits the amount of data we can send on one message var allPlayers = clan.Players.OrderByDescending(p => p.MonthBattles).ThenByDescending(p => p.TotalBattles).ToArray(); const int pageSize = 15; var pages = allPlayers.Length / pageSize + 1; color = clan.TotalWn8.ToColor(); for (var currentPage = 0; currentPage < pages; ++currentPage) { sb.Clear(); title = (clan.ClanTag.EqualsCiAi(clan.Name) ? clan.ClanTag : $"{clan.ClanTag} - {clan.Name}") + $" - Page {currentPage + 1} of {pages}"; sb.Append($"All members of the `{clan.ClanTag}`"); if (!string.IsNullOrWhiteSpace(clan.Country)) { sb.Append($" ({clan.Country.ToUpperInvariant()})"); } sb.AppendLine($", on the {clan.Plataform}:"); sb.AppendLine(); foreach (var p in allPlayers.Skip(currentPage * pageSize).Take(pageSize)) { sb.AppendLine($"{Formatter.MaskedUrl(p.Name, new Uri(p.PlayerOverallUrl))}, {p.MonthBattles} battles, WN8: {p.MonthWn8:N0}"); } embed = new DiscordEmbedBuilder { Title = title, Description = sb.ToString(), Color = new DiscordColor(color.R, color.G, color.B), Url = $"https://{platformPrefix}wotclans.com.br/Clan/{clan.ClanTag}", Author = new DiscordEmbedBuilder.EmbedAuthor { Name = "WoTClans", Url = $"https://{platform}wotclans.com.br" }, Footer = new DiscordEmbedBuilder.EmbedFooter { Text = $"Page {currentPage + 1} of {pages}" } }; await ctx.RespondAsync("", embed : embed); } } }
public async Task ClanInactives(CommandContext ctx, [Description("The clan **tag**")] string clanTag) { try { if (!await CanExecute(ctx, Features.Clans)) { return; } await ctx.TriggerTypingAsync(); if (string.IsNullOrWhiteSpace(clanTag)) { await ctx.RespondAsync($"You must send a clan tag as parameter, {ctx.User.Mention}."); return; } Log.Debug($"Requesting {nameof(ClanInactives)}({clanTag})..."); var cfg = GuildConfiguration.FromGuild(ctx.Guild); var platform = GetPlatform(clanTag, cfg.Plataform, out clanTag); clanTag = clanTag.Trim('[', ']'); clanTag = clanTag.ToUpperInvariant(); if (!ClanTagRegex.IsMatch(clanTag)) { await ctx.RespondAsync($"You must send a **valid** clan **tag** as parameter, {ctx.User.Mention}."); return; } var provider = new DbProvider(_connectionString); var clan = provider.GetClan(platform, clanTag); if (clan == null) { platform = platform == Platform.PS ? Platform.XBOX : Platform.PS; clan = provider.GetClan(platform, clanTag); if (clan == null) { await ctx.RespondAsync( $"Can't find on a clan with tag `[{clanTag}]`, {ctx.User.Mention}. Maybe my site doesn't track it yet... or you have the wrong clan tag."); return; } } if (!clan.Enabled) { await ctx.RespondAsync($"Data collection for this clan is **disabled**, the reason for this is {clan.DisabledReason}, {ctx.User.Mention}."); return; } var inactives = clan.Players.Where(p => !p.IsActive).ToArray(); if (inactives.Length <= 0) { await ctx.RespondAsync($"There are no inactive tankers on this clan, {ctx.User.Mention}."); return; } // To retrieve the last battle for (int i = 0; i < inactives.Length; i++) { inactives[i] = provider.GetPlayer(inactives[i].Id, true); } inactives = inactives.OrderBy(p => p.MonthBattles).ThenBy(p => p.LastBattle ?? DateTime.Today.AddYears(-5)).ToArray(); var sb = new StringBuilder(); sb.Append($"Information about `{clan.ClanTag}`'s {inactives.Length} inactives tankers on the {clan.Plataform}, {ctx.User.Mention}:"); sb.AppendLine(); var maxNameLength = inactives.Max(p => p.Name.Length); sb.AppendLine("```"); sb.AppendLine($"{platform.TagName().PadRight(maxNameLength)} {"Days".PadLeft(5)} {"Battles".PadLeft(7)} {"WN8".PadLeft(6)}"); foreach (var p in inactives.Take(30)) { sb.AppendLine($"{(p.Name ?? string.Empty).PadRight(maxNameLength)} {(DateTime.UtcNow - (p.LastBattle ?? DateTime.Today.AddYears(-5))).TotalDays.ToString("N0").PadLeft(5)} {p.MonthBattles.ToString("N0").PadLeft(7)} {p.TotalWn8.ToString("N0").PadLeft(6)}"); } sb.AppendLine("```"); var color = clan.InactivesWn8.ToColor(); var platformPrefix = clan.Plataform == Platform.PS ? "ps." : string.Empty; var embed = new DiscordEmbedBuilder { Title = $"{clan.ClanTag}'s Inactives Tankers", Description = sb.ToString(), Color = new DiscordColor(color.R, color.G, color.B), Url = $"https://{platformPrefix}wotclans.com.br/Clan/{clan.ClanTag}", Author = new DiscordEmbedBuilder.EmbedAuthor { Name = "WoTClans", Url = $"https://{platformPrefix}wotclans.com.br" }, Footer = new DiscordEmbedBuilder.EmbedFooter { Text = $"Calculated at {DateTime.UtcNow:yyyy-MM-dd HH:mm} UTC" } }; await ctx.RespondAsync("", embed : embed); } catch (Exception ex) { Log.Error($"Error calling {nameof(ClanInactives)}({clanTag})", ex); await ctx.RespondAsync( $"Sorry, {ctx.User.Mention}. There was an error... the *Coder* will be notified of `{ex.Message}`."); } }
public async Task Damage(CommandContext ctx, [Description("The Tank name, as it appears in battles. If it has spaces, enclose it on quotes.")][RemainingText] string tankName) { if (!await CanExecute(ctx, Features.Tanks)) { return; } Log.Info($"Requesting {nameof(Damage)}({tankName})"); await ctx.TriggerTypingAsync(); var cfg = GuildConfiguration.FromGuild(ctx.Guild); var tank = FindTank(cfg.Plataform, tankName, out var exact); if (tank == null) { await ctx.RespondAsync($"Can't find a tank with `{tankName}` on the name, {ctx.User.Mention}."); return; } var provider = new DbProvider(_connectionString); var tr = provider.GetTanksReferences(tank.Plataform, null, tank.TankId, false, false, false).FirstOrDefault(); if (tr == null) { await ctx.RespondAsync($"Sorry, there is no tank statistics for the `{tank.Name}`, {ctx.User.Mention}."); return; } var sb = new StringBuilder(); sb.AppendLine($"Here the **Target Damage** information about the `{tr.Name}`, Tier {tr.Tier.ToRomanNumeral()}, {tr.Nation.GetDescription()}, {(tr.IsPremium ? "Premium" : "Regular")}, {ctx.User.Mention}:"); sb.AppendLine(); sb.AppendLine("```"); sb.AppendLine("Rating WN8 Damage Piercings Shots"); sb.AppendLine($"Average { ((int)Wn8Rating.Average).ToString("N0").PadLeft(5)} { tr.TargetDamageAverage.ToString("N0").PadLeft(6)} { tr.TargetDamageAveragePiercings.ToString("N0").PadLeft(2)} {tr.TargetDamageAverageShots.ToString("N0").PadLeft(2)}"); sb.AppendLine($"Good { ((int)Wn8Rating.Good).ToString("N0").PadLeft(5)} { tr.TargetDamageGood.ToString("N0").PadLeft(6)} { tr.TargetDamageGoodPiercings.ToString("N0").PadLeft(2)} {tr.TargetDamageGoodShots.ToString("N0").PadLeft(2)}"); sb.AppendLine($"Great { ((int)Wn8Rating.Great).ToString("N0").PadLeft(5)} { tr.TargetDamageGreat.ToString("N0").PadLeft(6)} { tr.TargetDamageGreatPiercings.ToString("N0").PadLeft(2)} {tr.TargetDamageGreatShots.ToString("N0").PadLeft(2)}"); sb.AppendLine($"Unicum { ((int)Wn8Rating.Unicum).ToString("N0").PadLeft(5)} { tr.TargetDamageUnicum.ToString("N0").PadLeft(6)} { tr.TargetDamageUnicumPiercings.ToString("N0").PadLeft(2)} {tr.TargetDamageUnicumShots.ToString("N0").PadLeft(2)}"); sb.AppendLine($"Super {((int)Wn8Rating.SuperUnicum).ToString("N0").PadLeft(5)} {tr.TargetDamageSuperUnicum.ToString("N0").PadLeft(6)} {tr.TargetDamageSuperUnicumPiercings.ToString("N0").PadLeft(2)} {tr.TargetDamageSuperUnicumShots.ToString("N0").PadLeft(2)}"); sb.AppendLine("```"); sb.AppendLine(); sb.AppendLine($"Recent Averages: WN8: {tr.LastMonth.AverageWn8:N0}, Win Rate: {tr.LastMonth.WinRatio:P1}, Direct Dmg: {tr.LastMonth.DamageDealt:N0}, " + $"Total Dmg: {tr.LastMonth.TotalDamage:N0}, Spots: {tr.LastMonth.Spotted:N1}, Kills: {tr.LastMonth.Kills:N1}."); var emoji = DiscordEmoji.FromName(ctx.Client, ":exclamation:"); if (!exact) { sb.AppendLine(); sb.AppendLine($"{emoji} If this is *not the tank* you are looking for, try sending the exact short name, the one that appears during battles, or enclosing the name in quotes."); } var platformPrefix = tr.Plataform == Platform.PS ? "ps." : string.Empty; var color = tr.LastMonth.AverageWn8.ToColor(); var embed = new DiscordEmbedBuilder { Title = $"{tank.Name} Target Damage", Description = sb.ToString(), Color = new DiscordColor(color.R, color.G, color.B), ThumbnailUrl = tank.SmallImageUrl, Url = tank.Url, Author = new DiscordEmbedBuilder.EmbedAuthor { Name = "WoTClans", Url = $"https://{platformPrefix}wotclans.com.br" }, Footer = new DiscordEmbedBuilder.EmbedFooter { Text = $"Calculated at {tr.Date:yyyy-MM-dd} from {tr.LastMonth.TotalPlayers:N0} recent players and {tr.LastMonth.TotalBattles:N0} battles." } }; await ctx.RespondAsync("", embed : embed); }
public async Task GetSiteStatus(CommandContext ctx, [Description("The site to query, *PS* or *XBOX*")] string plataformString = "XBOX") { await ctx.TriggerTypingAsync(); var userId = ctx?.User?.Id ?? 0; Log.Info($"Requesting {nameof(GetSiteStatus)} by {userId}..."); if (userId != _coder) { var emoji = DiscordEmoji.FromName(ctx.Client, ":no_entry:"); var embed = new DiscordEmbedBuilder { Title = "Access denied", Description = $"{emoji} You may be a *coder*, but you are not **The Coder**!", Color = DiscordColor.Red }; await ctx.RespondAsync("", embed : embed); return; } var cfg = GuildConfiguration.FromGuild(ctx.Guild); var plataform = GetPlataform(plataformString + ".", cfg.Plataform, out var _); try { var cacheDirectory = ConfigurationManager.AppSettings["CacheDir"] ?? Path.GetTempPath(); var webCacheAge = TimeSpan.FromHours(4); var appId = ConfigurationManager.AppSettings["WgAppId"] ?? "demo"; var fetcher = new Fetcher(cacheDirectory) { ApplicationId = appId, WebCacheAge = webCacheAge, WebFetchInterval = TimeSpan.FromSeconds(1) }; var s = fetcher.GetSiteDiagnostic(plataform, ConfigurationManager.AppSettings["ApiAdminKey"]); var sb = new StringBuilder(); sb.AppendLine($"Data Age Minutes: {s.DataAgeMinutes:N0}"); sb.AppendLine($"Most Recent Clan Moment: {s.MostRecentClanMoment:yyyy-MM-dd HH:mm}"); sb.AppendLine($"Tank Leaders Last Date: {s.TankLeadersLastDate:yyyy-MM-dd}"); sb.AppendLine($"Tank MoE Last Date: {s.TanksMoELastDate:yyyy-MM-dd}"); sb.AppendLine($"Players: {s.PlayersCount:N0}; Clans: {s.ClansCount:N0}"); sb.AppendLine($"Clans With Players Updated On Last Hour: {s.ClansWithPlayersUpdatedOnLastHour:N0}"); sb.AppendLine($"Since Started Load: {s.AveragedProcessCpuUsage.SinceStartedLoad:P1}"); var plataformPrefix = plataform == Plataform.PS ? "ps." : string.Empty; var embed = new DiscordEmbedBuilder { Title = $"Site Status - {plataform.ToString().ToUpperInvariant()}", Description = sb.ToString(), Color = DiscordColor.Goldenrod, Author = new DiscordEmbedBuilder.EmbedAuthor { Name = "WoTClans", Url = $"https://{plataformPrefix}wotclans.com.br" }, Footer = new DiscordEmbedBuilder.EmbedFooter { Text = $"Retrieved at {s.ServerMoment:yyyy-MM-dd HH:mm} UTC." } }; await ctx.RespondAsync("", embed : embed); Log.Debug($"{nameof(GetSiteStatus)} returned ok."); } catch (Exception ex) { Log.Error($"{nameof(GetSiteStatus)}", ex); await ctx.RespondAsync($"Sorry, {ctx.User.Mention}. There was an error... the *Coder* will be notified of `{ex.Message}`."); return; } }
public async Task LeaderByFlag(CommandContext ctx, [Description("The clan flag **code**. As they appears on https://wotclans.com.br/Flags")] string flagCode, [Description("The Tank name, as it appears in battles. If it has spaces, enclose it on quotes.")] string tankName, [Description("A gamer tag, to be searched on the leaderboard of this tank. If it has spaces, enclose it in quotes.")][RemainingText] string gamerTag) { if (!await CanExecute(ctx, Features.Tanks)) { return; } Log.Info($"Requesting {nameof(LeaderByFlag)}({flagCode}, {tankName}, {gamerTag})"); await ctx.TriggerTypingAsync(); if (string.IsNullOrWhiteSpace(flagCode) || flagCode.Length != 2) { await ctx.RespondAsync($"Choose a valid flag code from {Formatter.MaskedUrl("the site", new Uri("https://wotclans.com.br/Flags"), "flags")}, {ctx.User.Mention}."); return; } flagCode = flagCode.RemoveDiacritics().ToLowerInvariant(); var cfg = GuildConfiguration.FromGuild(ctx.Guild); var tank = FindTank(cfg.Plataform, tankName, out var exact); if ((tank == null) && !string.IsNullOrWhiteSpace(gamerTag) && (gamerTag.Length <= 2)) { // Maybe the gamer tag is not a gamer tag, just the final part of a tank name... tank = FindTank(cfg.Plataform, tankName + " " + gamerTag, out exact); if (tank != null) { // yeah... it was not a gamer tag gamerTag = string.Empty; } } if (tank == null) { await ctx.RespondAsync($"Can't find a tank with `{tankName}` on the name, {ctx.User.Mention}."); return; } Log.Info($"Requested {nameof(LeaderByFlag)}({flagCode}, {tank.Plataform}.{tank.Name}, {gamerTag})"); int top = 25; gamerTag = gamerTag ?? string.Empty; gamerTag = gamerTag.Replace("\"", "").ToLowerInvariant(); if (!string.IsNullOrEmpty(gamerTag)) { top = 1000; } var provider = new DbProvider(_connectionString); if (gamerTag.EqualsCiAi("me")) { var playerId = provider.GetPlayerIdByDiscordId((long)ctx.User.Id); if (playerId.HasValue) { var player = provider.GetPlayer(playerId.Value); gamerTag = player.Name; } } var leaderboard = provider.GetLeaderboard(tank.Plataform, tank.TankId, top, flagCode).Where(l => (l.ClanFlag ?? string.Empty) .ToLowerInvariant() == flagCode).ToArray(); if (!leaderboard.Any()) { await ctx.RespondAsync($"Sorry, there is no leaderboard information for the `{tank.Name}`, {ctx.User.Mention}."); return; } var sb = new StringBuilder(); sb.AppendLine($"Here the top {Math.Min(leaderboard.Length, 25)} players from {flagCode.ToUpperInvariant()} on the `{tank.Name}`, Tier {tank.Tier.ToRomanNumeral()}, {tank.Nation.GetDescription()}, {(tank.IsPremium ? "Premium" : "Regular")}, {ctx.User.Mention}:"); for (int i = 0; i < Math.Min(leaderboard.Length, 25); i++) { var l = leaderboard[i]; sb.AppendLine($"{(i + 1).ToString().PadLeft(3)}: `{l.GamerTag}`, " + $"from `{l.ClanTag}`. Dmg = {l.TotalDamage:N0} = {l.DirectDamage:N0} + {l.DamageAssisted:N0}"); } if (!string.IsNullOrWhiteSpace(gamerTag)) { Leader leader = null; int position = 1; for (int i = 0; i < leaderboard.Length; i++) { var l = leaderboard[i]; if (l.GamerTag.ToLowerInvariant() == gamerTag.ToLowerInvariant()) { position = i + 1; leader = l; break; } } if (leader != null) { sb.AppendLine(); sb.AppendLine($" {Formatter.MaskedUrl(leader.GamerTag, new Uri(leader.PlayerOverallUrl), leader.GamerTag)} is in {position}th. Dmg = {leader.TotalDamage:N0} = {leader.DirectDamage:N0} + {leader.DamageAssisted:N0}"); } else { sb.AppendLine(); sb.AppendLine($"`{gamerTag}` does not appear on any place up to {leaderboard.Length:N0}th."); } } var emoji = DiscordEmoji.FromName(ctx.Client, ":exclamation:"); if (!exact) { sb.AppendLine(); sb.AppendLine($"{emoji} If this is *not the tank* you are looking for, try sending the exact short name, the one that appears during battles, or enclosing the name in quotes."); } var platformPrefix = leaderboard.First().Plataform == Platform.PS ? "ps." : string.Empty; var embed = new DiscordEmbedBuilder { Title = $"{tank.Name} Leaderboard for {flagCode.ToUpperInvariant()}", Description = sb.ToString(), Color = DiscordColor.Gray, ThumbnailUrl = tank.SmallImageUrl, Url = $"https://{platformPrefix}wotclans.com.br/Tanks/{tank.TankId}", Author = new DiscordEmbedBuilder.EmbedAuthor { Name = "WoTClans", Url = $"https://{platformPrefix}wotclans.com.br" }, Footer = new DiscordEmbedBuilder.EmbedFooter { Text = $"Calculated at {leaderboard.First().Date:yyyy-MM-dd}." } }; await ctx.RespondAsync("", embed : embed); }
public async Task TankerTank(CommandContext ctx, [Description("The *gamer tag* or *PSN Name*. If it has spaces, enclose it on double quotes.")] string gamerTag, [Description("The Tank name, as it appears in battles. If it has spaces, enclose it on double quotes.")][RemainingText] string tankName) { // Yeah... it's a Player featurem but it uses more calculations for tanks... if (!await CanExecute(ctx, Features.Players)) { return; } if (string.IsNullOrWhiteSpace(gamerTag)) { await ctx.RespondAsync($"Plase specify the *Gamer Tag*, {ctx.User.Mention}. Something like `!w tanker {ctx.User.Username.RemoveDiacritics()}`, for example."); return; } await ctx.TriggerTypingAsync(); Log.Debug($"Requesting {nameof(TankerTank)}({gamerTag}, {tankName})..."); try { var cfg = GuildConfiguration.FromGuild(ctx.Guild); var provider = new DbProvider(_connectionString); var playerCommands = new PlayerCommands(); var player = await playerCommands.GetPlayer(ctx, gamerTag); if (player == null) { Log.Debug($"Could not find player {gamerTag}"); await ctx.RespondAsync($"I could not find a tanker " + $"with the Gamer Tag `{gamerTag}` on the Database, {ctx.User.Mention}. I may not track this player, or the Gamer Tag is wrong."); return; } gamerTag = player.Name; var tank = FindTank(player.Plataform, tankName, out var exact); if (tank == null) { await ctx.RespondAsync($"Can't find a tank with `{tankName}` on the name, {ctx.User.Mention}."); return; } var tr = provider.GetTanksReferences(tank.Plataform, null, tank.TankId, false, false, false).FirstOrDefault(); if (tr == null) { await ctx.RespondAsync($"Sorry, there is no tank statistics for the `{tank.Name}`, {ctx.User.Mention}."); return; } var hist = provider.GetPlayerHistoryByTank(player.Plataform, player.Id, tank.TankId).ToArray(); if (!hist.Any()) { await ctx.RespondAsync($"Sorry, there is no tank statistics history for the `{tank.Name}` for the player `{gamerTag}`, {ctx.User.Mention}."); return; } var wn8Expected = provider.GetWn8ExpectedValues(player.Plataform); foreach (var h in hist) { h.Wn8 = wn8Expected.CalculateWn8(h); } Tanks.TankPlayerStatistics[] head, tail; if (hist.Length > 20) { head = hist.Take(15).ToArray(); tail = hist.Skip(hist.Length - 5).Take(5).ToArray(); } else { head = hist; tail = null; } var sb = new StringBuilder(); sb.AppendLine($"History of the {Formatter.MaskedUrl(tr.Name, new Uri(tr.Url))}, Tier {tr.Tier.ToRomanNumeral()}, {(tr.IsPremium ? "Premium" : "Regular")}, as played by {Formatter.MaskedUrl(gamerTag, new Uri(player.PlayerOverallUrl))}, {ctx.User.Mention}:"); sb.AppendLine(); sb.AppendLine("```"); sb.AppendLine("Date Battles Tot.Dmg WN8"); foreach (var t in head) { sb.AppendLine($"{t.LastBattle:yyyy-MM-dd} {t.Battles.ToString("N0").PadLeft(7)} {t.TotalDamagePerBattle.ToString("N0").PadLeft(7)} {t.Wn8.ToString("N0").PadLeft(6)}"); } if (tail != null) { sb.AppendLine("..."); foreach (var t in tail) { sb.AppendLine($"{t.LastBattle:yyyy-MM-dd} {t.Battles.ToString("N0").PadLeft(7)} {t.TotalDamagePerBattle.ToString("N0").PadLeft(7)} {t.Wn8.ToString("N0").PadLeft(6)}"); } } sb.AppendLine("```"); sb.AppendLine(); var ptr = player.Performance.All[tr.TankId]; sb.AppendLine($"**{player.Name}** current values:"); sb.AppendLine($"Total Damage: {ptr.TotalDamagePerBattle:N0} "); sb.AppendLine($"Direct Damage: {ptr.DirectDamagePerBattle:N0} "); sb.AppendLine($"Assited Damage: {ptr.DamageAssistedPerBattle:N0} "); sb.AppendLine($"WN8: {ptr.Wn8:N0}; Win Rate: {ptr.WinRate:P1}"); sb.AppendLine($"Battles: {ptr.Battles:N0}; Hours Battling: {ptr.BattleLifeTime.TotalHours:N0}"); sb.AppendLine($"Max Kills: {ptr.MaxFrags:N1}; Avg Kills: {ptr.KillsPerBattle:N1}"); sb.AppendLine(); sb.AppendLine($"**Global Recent** average among {tr.LastMonth.TotalPlayers:N0} players of the {tr.Name}:"); sb.AppendLine($"Total Damage: {tr.LastMonth.TotalDamage:N0} "); sb.AppendLine($"Direct Damage: {tr.LastMonth.DamageDealt:N0} "); sb.AppendLine($"Assited Damage: {tr.LastMonth.DamageAssisted:N0} "); sb.AppendLine($"WN8: {tr.LastMonth.AverageWn8:N0}; Win Rate: {tr.LastMonth.WinRatio:P1}"); sb.AppendLine($"Battles: {tr.LastMonth.BattlesPerPlayer:N0}; Hours Battling: {tr.LastMonth.TimePerPlayer.TotalHours:N0}"); sb.AppendLine($"Max Kills: {tr.LastMonth.MaxKills:N1}; Avg Kills: {tr.LastMonth.Kills:N1}"); var plataformPrefix = tr.Plataform == Plataform.PS ? "ps." : string.Empty; var color = ptr.Wn8.ToColor(); var embed = new DiscordEmbedBuilder { Title = $"{gamerTag} history with the {tank.Name}", Description = sb.ToString(), Color = new DiscordColor(color.R, color.G, color.B), ThumbnailUrl = tank.SmallImageUrl, Url = player.PlayerOverallUrl, Author = new DiscordEmbedBuilder.EmbedAuthor { Name = "WoTClans", Url = $"https://{plataformPrefix}wotclans.com.br" }, Footer = new DiscordEmbedBuilder.EmbedFooter { Text = $"Calculatet at {player.Moment:yyyy-MM-dd HH:mm} UTC" } }; await ctx.RespondAsync("", embed : embed); } catch (Exception ex) { Log.Error($"{nameof(TankerTank)}({gamerTag})", ex); await ctx.RespondAsync($"Sorry, {ctx.User.Mention}. There was an error... the *Coder* will be notified of `{ex.Message}`."); return; } }