private async Task CmdListModsAsync(SocketCommandContext context, CancellationToken cancellationToken = default) { using IDisposable logScope = _log.BeginCommandScope(context, this); IOrderedEnumerable <StellarisMod> mods = (await _stellarisModsStore.GetAllAsync(cancellationToken).ConfigureAwait(false)).OrderBy(m => m.Name); if (!mods.Any()) { await context.ReplyAsync($"{_einherjiOptions.FailureSymbol} You did not have any mod on the list.", cancellationToken).ConfigureAwait(false); return; } List <string> listStrings = new List <string>(mods.Count()); foreach (StellarisMod mod in mods) { listStrings.Add(ModToListString(mod, listStrings.Count + 1)); } EmbedBuilder embed = new EmbedBuilder() .WithDescription(string.Join('\n', listStrings)) .WithFooter($"Currently you guys are using {listStrings.Count} mods.", context.Client.CurrentUser.GetAvatarUrl()) .WithTimestamp(DateTimeOffset.Now) .WithAuthor("Your Stellaris mods", context.Client.CurrentUser.GetAvatarUrl()); await context.ReplyAsync(null, false, embed.Build(), cancellationToken).ConfigureAwait(false); }
private async Task CmdRandomizeStatus(SocketCommandContext context, CancellationToken cancellationToken = default) { EinherjiOptions einherjiOptions = this._einherjiOptions.CurrentValue; if (context.User.Id != einherjiOptions.AuthorID) { await context.ReplyAsync($"{einherjiOptions.FailureSymbol} You have no rights to tell me ~~how to live my life~~ do this!", cancellationToken).ConfigureAwait(false); return; } Status status = await RandomizeStatusAsync(context.Client, cancellationToken).ConfigureAwait(false); if (status != null) { await context.ReplyAsync($"{einherjiOptions.SuccessSymbol} Status changed: `{status.Text.Replace("`", "\\`")}`", cancellationToken).ConfigureAwait(false); // restart loop StartBackgroundLoop(); } else if (this._options.CurrentValue.Statuses?.Any() != true) { await context.ReplyAsync($"{einherjiOptions.FailureSymbol} Status not changed - ensure status list is not empty.", cancellationToken).ConfigureAwait(false); } }
private Task CmdHelpAsync(SocketCommandContext context, CancellationToken cancellationToken = default) { using IDisposable logScope = _log.BeginCommandScope(context, this); PiholeOptions options = _piholeOptions.CurrentValue; IReadOnlyDictionary <string, PiholeInstanceOptions> instances = options.GetUserAuthorizedInstances(context.User); // check user is authorized to manage any instance if (instances?.Any() != true) { return(context.ReplyAsync($"{_einherjiOptions.CurrentValue.FailureSymbol} You are not authorized to manage any of Kathara PiHole instance.", cancellationToken)); } string prefix = _commandsOptions.CurrentValue.Prefix; EmbedBuilder embed = new EmbedBuilder() .WithThumbnailUrl("https://upload.wikimedia.org/wikipedia/en/thumb/1/15/Pi-hole_vector_logo.svg/1200px-Pi-hole_vector_logo.svg.png") .WithDescription("Management utility for PiHoles present in Kathara network"); embed.AddField("Commands", $"**{prefix}pihole <instance id>** - show info on specific PiHole instance\n" + $"**{prefix}pihole <instance id> enable** - enable a specific PiHole instance\n" + $"**{prefix}pihole <instance id> disable** - disable a specific PiHole instance for {options.DefaultDisableTime.TotalMinutes} minutes\n" + $"**{prefix}pihole <instance id> disable <minutes>** - disable a specific PiHole instance for custom amount of minutes\n" + $"**{prefix}pihole** - display this message and check which instances you can manage"); embed.AddField("Instances you can manage", string.Join(", ", instances.Keys)); return(context.ReplyAsync(null, false, embed.Build(), cancellationToken)); }
public async Task SearchAsync(SocketCommandContext context, DbService db) { var user = await db.Users.FindAsync(context.User.Id) ?? await StartAsync(context, db); var enemies = await db.Enemies.Where(x => x.ZoneId == user.ZoneId).ToListAsync(); var found = _random.Next(100); if (found >= 40) { var enemy = enemies[_random.Next(enemies.Count)]; var embed = new EmbedBuilder { Color = Color.Green, Description = $"{context.User.Mention} encountered a wild {enemy.Name}!", ImageUrl = enemy.Image, Footer = new EmbedFooterBuilder { Text = "Combat starting shortly..." } }; await context.ReplyAsync(embed); await BattleAsync(context, user, enemy, db); } else { await context.ReplyAsync($"{context.User.Mention} searched around and found no one", Color.Red.RawValue); } }
private Task CmdIntelGuildAsync(SocketCommandContext context, CancellationToken cancellationToken = default) { using IDisposable logScope = _log.BeginCommandScope(context, this); if (context.IsPrivate) { return(context.ReplyAsync($"{_einherjiOptions.CurrentValue.FailureSymbol} This command can only be used in a guild channel.", cancellationToken)); } EmbedBuilder embed = new EmbedBuilder(); AddGuildInfo(embed, context.Guild); return(context.ReplyAsync(null, false, embed.Build(), cancellationToken)); }
private async Task CmdRemoveModAsync(SocketCommandContext context, Match match, CancellationToken cancellationToken = default) { using IDisposable logScope = _log.BeginCommandScope(context, this); if (context.User.Id != _einherjiOptions.AuthorID) { await context.ReplyAsync($"{_einherjiOptions.FailureSymbol} You can't order me to do that.", cancellationToken).ConfigureAwait(false); return; } if (match.Groups.Count < 2) { await context.ReplyAsync($"{_einherjiOptions.FailureSymbol} Please specify numbers of mods to remove. Can be multiple numbers separated with spaces.\nTo get numbers of mods, use `{_commandsOptions.Prefix}stellaris mods`", cancellationToken).ConfigureAwait(false); return; } IOrderedEnumerable <StellarisMod> mods = (await _stellarisModsStore.GetAllAsync(cancellationToken).ConfigureAwait(false)).OrderBy(m => m.Name); string[] idStrings = match.Groups[1].Value.Split(' ', StringSplitOptions.RemoveEmptyEntries); List <string> invalidIdStrings = new List <string>(idStrings.Length); List <StellarisMod> removalList = new List <StellarisMod>(idStrings.Length); for (int i = 0; i < idStrings.Length; i++) { if (!int.TryParse(idStrings[i], out int id)) { invalidIdStrings.Add(idStrings[i]); } else { removalList.Add(mods.ElementAt(id)); } } string incompatibleString = invalidIdStrings.Count != 0 ? $"\nFollowing IDs are invalid: {string.Join(", ", invalidIdStrings.Select(s => $"`{s}`"))}." : null; if (removalList.Any()) { _log.LogDebug("Removing {Count} stellaris mods: {ModNames}", removalList.Count, string.Join(", ", removalList.Select(s => s.Name))); await _stellarisModsStore.RemoveAsync(removalList, cancellationToken).ConfigureAwait(false); await context.ReplyAsync($"{_einherjiOptions.SuccessSymbol} Removed {removalList.Count} mods.{incompatibleString}", cancellationToken).ConfigureAwait(false); } else { await context.ReplyAsync($"No mods removed.{incompatibleString}", cancellationToken).ConfigureAwait(false); } }
private async Task CmdRemoveGameAsync(SocketCommandContext context, Match match, CancellationToken cancellationToken = default) { using IDisposable logScope = _log.BeginCommandScope(context, this); if (context.User.Id != _einherjiOptions.CurrentValue.AuthorID) { await SendInsufficientPermissionsAsync(context.Channel, cancellationToken).ConfigureAwait(false); return; } if (match.Groups.Count < 2 || match.Groups[1]?.Length < 1) { await SendNameRequiredAsync(context.Channel, cancellationToken).ConfigureAwait(false); return; } // check if game exists string gameName = match.Groups[1].Value.Trim(); _log.LogDebug("Creating patchbot game {GameName}", gameName); PatchbotGame game = await _patchbotGamesStore.GetAsync(gameName, cancellationToken).ConfigureAwait(false); if (game == null) { _log.LogDebug("Patchbot game {GameName} not found", gameName); await SendGameNotFoundAsync(context.Channel, gameName, cancellationToken).ConfigureAwait(false); return; } await _patchbotGamesStore.DeleteAsync(game, cancellationToken).ConfigureAwait(false); await context.ReplyAsync($"{_einherjiOptions.CurrentValue.SuccessSymbol} Game `{game.Name}` removed.", cancellationToken).ConfigureAwait(false); }
private async Task CmdUnsubscribeAsync(SocketCommandContext context, Match match, CancellationToken cancellationToken = default) { using IDisposable logScope = _log.BeginCommandScope(context, this); if (match.Groups.Count < 2 || match.Groups[1]?.Length < 1) { await SendNameRequiredAsync(context.Channel, cancellationToken).ConfigureAwait(false); return; } string gameName = match.Groups[1].Value.Trim(); _log.LogDebug("Unsubscribing user {UserID} to patchbot game {GameName}", context.User.Id, gameName); PatchbotGame game = await _patchbotGamesStore.GetAsync(gameName, cancellationToken).ConfigureAwait(false); if (game == null) { _log.LogDebug("Patchbot game {GameName} not found", gameName); await SendGameNotFoundAsync(context.Channel, gameName, cancellationToken).ConfigureAwait(false); return; } if (game.SubscriberIDs.Remove(context.User.Id)) { await _patchbotGamesStore.DeleteAsync(game, cancellationToken).ConfigureAwait(false); } await context.ReplyAsync($"{_einherjiOptions.CurrentValue.SuccessSymbol} You will no longer be pinged about `{game.Name}` updates.", cancellationToken).ConfigureAwait(false); }
private Task CmdCommandsAsync(SocketCommandContext context, Match match, CancellationToken cancellationToken = default) { IOrderedEnumerable <IGrouping <string, CommandDescriptor> > commands = this.GetCommandDescriptors(context); if (commands.Any()) { EmbedBuilder embed = this.StartEmbed(context); string prefix = this.GetPrefix(context); StringBuilder commandsList = new StringBuilder(); foreach (IGrouping <string, CommandDescriptor> group in commands) { commandsList.Clear(); foreach (CommandDescriptor cmd in group) { commandsList.Append($"***{prefix}{cmd.DisplayName}***: {cmd.Summary}\n"); } embed.AddField(group.Key, commandsList.ToString(), inline: false); } return(context.ReplyAsync(null, false, embed.Build(), cancellationToken)); } else { return(context.InlineReplyAsync($"{_einherjiOptions.FailureSymbol} Ooops, I detected no commands... this obviously isn't right. Please let {GetAuthorText(context)} know!", cancellationToken)); } }
private Task CmdGetAsync(SocketCommandContext context, Match match, CancellationToken cancellationToken = default) { EmbedBuilder embed = this.StartEmbed(context); embed.AddField("Commands", $"Use **{this.GetPrefix(context)}commands** to get list of commands that you can use here!", inline: false); if (this.IsMainRestrictionGroup(context)) { embed.AddField("Additional features", "If you try to use an another bot in a wrong channel, I'll direct you to the correct channel.\n" + $"I'll automatically post new or just finished Elite Dangerous Community Goals in {MentionUtils.MentionChannel(_eliteOptions.AutoNewsChannelID)}.\n" + $"I'll post a message in {GetLeaveChannel(context)} when a user leaves the guild.", inline: false); } else { embed.AddField("Additional features", $"If I have permissions to post in {GetLeaveChannel(context)}, I'll post a message whenever a user leaves the guild.\n" + $"More additional features are provided in {GetAuthorText(context)}'s server.", inline: false); } embed.AddField("Support", $"To submit bugs or suggestions, please open an issue on [GitHub](https://github.com/TehGM/EinherjiBot/issues). Alternatively, you can message {GetAuthorText(context)}.\n" + "To support the developer, consider donating on [GitHub Sponsors](https://github.com/sponsors/TehGM), [Patreon](https://patreon.com/TehGMdev) or [Buy Me A Coffee](https://www.buymeacoffee.com/TehGM). **Thank you!**", inline: false); return(context.ReplyAsync(null, false, embed.Build(), cancellationToken)); }
private async Task PerformActionOnAllAsync(SocketCommandContext context, VoiceChannelMatch channelMatch, VoiceChannelAction action, CancellationToken cancellationToken) { if (!channelMatch.IsSuccess) { return; } // verify it's a guild message SocketTextChannel responseChannel = await this.VerifyGuildChannelAsync(context, cancellationToken).ConfigureAwait(false); if (responseChannel == null) { return; } // verify user's permissions _log.LogTrace("Verifying user {ID} has {Permission} permission in channel {ChannelName} ({ChannelFromID})", channelMatch.User.Id, action.RequiredPermission, channelMatch.Channel.Name, channelMatch.Channel.Id); if (!await this.VerifyUserPermissionsAsync(context, channelMatch.Channel, channelMatch.User.Id, action.RequiredPermission, cancellationToken).ConfigureAwait(false)) { return; } // verify bot's permissions _log.LogTrace("Verifying the bot has {Permission} permission in channel {ChannelName} ({ChannelFromID})", action.RequiredPermission, channelMatch.Channel.Name, channelMatch.Channel.Id); if (!await this.VerifyUserPermissionsAsync(context, channelMatch.Channel, context.Client.CurrentUser.Id, action.RequiredPermission, cancellationToken).ConfigureAwait(false)) { return; } // perform the action on the users SocketGuildUser[] users = channelMatch.Channel.Users.ToArray(); string channelMention = GetVoiceChannelMention(channelMatch.Channel); _log.LogDebug($"{action.ModeWord} {{Count}} users from channel {{ChannelName}} ({{ChannelID}})", users.Length, channelMatch.Channel.Name, channelMatch.Channel.Id); RestUserMessage response = await context.ReplyAsync($"{action.ModeWord} {users.Length} user{(users.Length > 1 ? "s" : null)} in {channelMention}.", cancellationToken).ConfigureAwait(false); int errorCount = 0; foreach (SocketGuildUser user in users) { try { await user.ModifyAsync(action.Action, cancellationToken).ConfigureAwait(false); } catch { errorCount++; } } // display confirmation StringBuilder builder = new StringBuilder(); int successCount = users.Length - errorCount; builder.AppendFormat("{0} user{1} {2} in {3}.", successCount.ToString(), successCount > 1 || successCount == 0 ? "s" : null, action.ActionCompletedWord, channelMention); if (errorCount > 0) { builder.AppendFormat("\nFailed to {3} {0} user{2}. {1}", errorCount.ToString(), this._einherjiOptions.CurrentValue.FailureSymbol, errorCount > 1 ? "s" : null, action.ActionFailedWord); } await response.ModifyAsync(props => props.Content = builder.ToString(), cancellationToken).ConfigureAwait(false); }
private async Task CmdAddModAsync(SocketCommandContext context, Match match, CancellationToken cancellationToken = default) { using IDisposable logScope = _log.BeginCommandScope(context, this); Task CreateInvalidUseResponse() => context.ReplyAsync($"{_einherjiOptions.FailureSymbol} Please specify both name and URL of the mod.\nProper usage of this command:\n***{_commandsOptions.Prefix}stellaris mods add <name> | <url>***", cancellationToken); if (context.User.Id != _einherjiOptions.AuthorID) { await context.ReplyAsync($"{_einherjiOptions.FailureSymbol} You can't order me to do that.", cancellationToken).ConfigureAwait(false); return; } if (match.Groups.Count < 3) { await CreateInvalidUseResponse().ConfigureAwait(false); return; } string name = match.Groups[1].Value.Trim(); string url = match.Groups[2].Value.Trim(); if (string.IsNullOrWhiteSpace(name) || string.IsNullOrWhiteSpace(url)) { await CreateInvalidUseResponse().ConfigureAwait(false); return; } if (url.Contains(' ', StringComparison.Ordinal)) { await context.ReplyAsync($"{_einherjiOptions.FailureSymbol} URL can't contain any spaces.", cancellationToken).ConfigureAwait(false); return; } StellarisMod mod = new StellarisMod(name, url); _log.LogDebug("Adding stellaris mod {ModName}", mod.Name); await _stellarisModsStore.AddAsync(mod, cancellationToken).ConfigureAwait(false); await context.ReplyAsync($"{_einherjiOptions.SuccessSymbol} Added mod:\n\n{ModToMessageString(mod)}", cancellationToken).ConfigureAwait(false); }
private Task CmdIntelHelpAsync(SocketCommandContext context, CancellationToken cancellationToken = default) { using IDisposable logScope = _log.BeginCommandScope(context, this); string prefix = _commandsOptions.CurrentValue.Prefix; EmbedBuilder embed = new EmbedBuilder() .AddField("Intel Commands", $"**{prefix}intel on me** - get intel on yourself\n" + $"**{prefix}intel on** ***<user ping>*** - get intel on pinged user\n" + $"**{prefix}intel on guild** - *(guild only)* get intel on current guild"); return(context.ReplyAsync(null, false, embed.Build(), cancellationToken)); }
private async Task CmdIntelUserAsync(SocketCommandContext context, Match match, CancellationToken cancellationToken = default) { using IDisposable logScope = _log.BeginCommandScope(context, this); string idString = match.Groups[1].Value; if (!ulong.TryParse(idString, out ulong id)) { await context.ReplyAsync($"{_einherjiOptions.CurrentValue.FailureSymbol} Could not parse user ID `{idString}`.", cancellationToken).ConfigureAwait(false); return; } IUser user = await _client.GetUserAsync(id).ConfigureAwait(false); if (user == null) { await context.ReplyAsync($"{_einherjiOptions.CurrentValue.FailureSymbol} Could not find user with ID `{id}`.", cancellationToken).ConfigureAwait(false); return; } await ProcessIntelUserAsync(context, user, cancellationToken).ConfigureAwait(false); }
private async Task CmdAddGameAsync(SocketCommandContext context, Match match, CancellationToken cancellationToken = default) { using IDisposable logScope = _log.BeginCommandScope(context, this); if (context.User.Id != _einherjiOptions.CurrentValue.AuthorID) { await SendInsufficientPermissionsAsync(context.Channel, cancellationToken).ConfigureAwait(false); return; } // get names if (match.Groups.Count < 2 || match.Groups[1]?.Length < 1) { await SendNameAndAliasesRequiredAsync(context.Channel, cancellationToken).ConfigureAwait(false); return; } string[] names = match.Groups[1].Value.Split(_namesSeparator, StringSplitOptions.RemoveEmptyEntries) .Select(name => name.Trim()).Where(name => !string.IsNullOrWhiteSpace(name)).ToArray(); if (names.Length == 0) { await SendNameAndAliasesRequiredAsync(context.Channel, cancellationToken).ConfigureAwait(false); return; } // check if game doesn't yet exist string gameName = names.First(); PatchbotGame game = await _patchbotGamesStore.GetAsync(gameName, cancellationToken).ConfigureAwait(false); if (game == null) { _log.LogDebug("Creating patchbot game {GameName}", gameName); game = new PatchbotGame(names.First(), names.TakeLast(names.Length - 1)); } // if it does, just add new aliases else { _log.LogDebug("Adding {AliasesCount} aliases to patchbot game {GameName}", names.Length, game.Name); for (int i = 0; i < names.Length; i++) { if (game.Aliases.Contains(names[i], StringComparer.OrdinalIgnoreCase)) { continue; } game.Aliases.Add(names[i]); } } await _patchbotGamesStore.SetAsync(game, cancellationToken).ConfigureAwait(false); await context.ReplyAsync($"{_einherjiOptions.CurrentValue.SuccessSymbol} Game `{game.Name}` updated.", cancellationToken).ConfigureAwait(false); }
private async Task CmdCommunityGoals(SocketCommandContext context, CancellationToken cancellationToken = default) { using IDisposable logScope = _log.BeginCommandScope(context, this); if (!this._enabled) { _log.LogDebug("Unable to handle Elite Dangerous Community Goals command - Inara credentials missing"); return; } IEnumerable <CommunityGoal> cgs = await QueryCommunityGoalsAsync(cancellationToken).ConfigureAwait(false); foreach (CommunityGoal cg in cgs) { await context.ReplyAsync(null, false, CommunityGoalToEmbed(cg).Build(), cancellationToken).ConfigureAwait(false); } }
private async Task CmdRetrieveAccountAsync(SocketCommandContext context, CancellationToken cancellationToken = default) { using IDisposable logScope = _log.BeginCommandScope(context, this); _log.LogDebug("Retrieving Netlix account credentials"); if (context.IsPrivate) { _log.LogTrace("Aborting Netflix account credentials retrieving: Group only"); await SendErrorAsync($"{_einherjiOptions.FailureSymbol} You can't do this in private message.\nGo to {GetAllowedChannelsMentionsText()}.", context.Channel).ConfigureAwait(false); return; } SocketGuildUser user = await context.Guild.GetGuildUserAsync(context.User).ConfigureAwait(false); if (!_netflixAccountOptions.CanRetrieve(user)) { _log.LogTrace("Aborting Netflix account credentials retrieving: User not privileged"); await SendErrorAsync($"{_einherjiOptions.FailureSymbol} You need {GetAllowedRolesMentionsText()} role to do this.", context.Channel).ConfigureAwait(false); return; } if (!_netflixAccountOptions.IsChannelAllowed(context.Channel)) { _log.LogTrace("Aborting Netflix account credentials retrieving: Wrong channel"); await SendErrorAsync($"You can't do this here.\nGo to {GetAllowedChannelsMentionsText()}.", context.Channel).ConfigureAwait(false); return; } // retrieve info from store NetflixAccount account = await _netflixAccountStore.GetAsync(cancellationToken).ConfigureAwait(false); // create message IUser modifiedByUser = null; if (account.ModifiedByID != null) { modifiedByUser = await context.Client.GetUserAsync(account.ModifiedByID.Value).ConfigureAwait(false); } EmbedBuilder embed = CreateConfirmationEmbed(account, modifiedByUser); string text = this.IsAutoRemoving ? GetAutoremoveText() : null; RestUserMessage sentMsg = await context.ReplyAsync(text, false, embed.Build(), cancellationToken).ConfigureAwait(false); // auto remove if (this.IsAutoRemoving) { RemoveMessagesDelayed(_netflixAccountOptions.AutoRemoveDelay, cancellationToken, sentMsg, context.Message); } }
private async Task ProcessIntelUserAsync(SocketCommandContext context, IUser user, CancellationToken cancellationToken) { UserData userData = await _userDataStore.GetAsync(user.Id, cancellationToken).ConfigureAwait(false); EmbedBuilder embed = new EmbedBuilder(); AddUserInfo(embed, user, userData); if (!context.IsPrivate) { SocketGuildUser guildUser = await context.Guild.GetGuildUserAsync(user.Id).ConfigureAwait(false); if (guildUser != null) { AddGuildUserInfo(embed, guildUser); } } await context.ReplyAsync(null, false, embed.Build(), cancellationToken).ConfigureAwait(false); }
public async Task <User> StartAsync(SocketCommandContext context, DbService db) { var user = new User { UserId = context.User.Id, Level = 1, Exp = 0, TotalExp = 0, Credit = 0, AttackMode = AttackType.Passive, ContinentId = 1, ZoneId = 1, UnspentTalentPoints = 0, DamageTalent = 0, HealthTalent = 0, WeaponId = 1, ArmorId = 2 }; await db.Users.AddAsync(user); await db.SaveChangesAsync(); var firstContinent = await db.Continents.FindAsync(1); var firstZone = await db.Zones.FindAsync(1); await context.ReplyAsync($"Welcome to Wonderland {context.User.Mention}!\n" + $"You're starting off within the continent {firstContinent.Name} in {firstZone.Name} where you'll face off monsters to gain experience to level up and explore other zones. You can also send troops on missions or do quests to gain experience and loot!\n" + "For every second level you level up, you'll receive a new talent point which you can spend to increase your health, or damage.\n\n" + "Now, wait a bit and we'll resume back to the command and below is a list of basic commands to get started!\n" + "Commands:\n" + "-Search (Find an opponement)"); await Task.Delay(2000); return(user); }
private async Task handler(SocketMessage arg) { if (!_isReady) { return; } var msg = arg as SocketUserMessage; if (msg == null) { return; } if (msg.Author.IsBot) { return; } int argPos = 0; if (!msg.HasMentionPrefix(_client.CurrentUser, ref argPos)) { return; } var context = new SocketCommandContext(_client, msg); _logger.Info($"Command ran by {context.User} in {context.Channel.Name} - {context.Message.Content}"); var result = await _commandService.ExecuteAsync(context, argPos, _dependencyMap, MultiMatchHandling.Best); if (result.IsSuccess) { await _dependencyMap.Get <WelcomeDbContext>().SaveChangesAsync(); return; } string response = null; switch (result) { case ParseResult parseResult: response = $":warning: There was an error parsing your command: `{parseResult.ErrorReason}`"; break; case PreconditionResult preconditionResult: response = $":warning: A prerequisite of your command failed: `{preconditionResult.ErrorReason}`"; break; case ExecuteResult executeResult: response = $":warning: Your command failed to execute. If this persists, contact a Discord Host.\n`{executeResult.Exception.Message}`"; _logger.Error(executeResult.Exception); break; } if (response != null) { await context.ReplyAsync(response); } }
private async Task CmdGetAsync(SocketCommandContext context, Match match, CancellationToken cancellationToken = default) { // check if command has game name using IDisposable logScope = _log.BeginCommandScope(context, this); if (match.Groups.Count < 2 || match.Groups[1]?.Length < 1) { await SendNameRequiredAsync(context.Channel, cancellationToken).ConfigureAwait(false); return; } // get server info string gameName = match.Groups[1].Value.Trim(); GameServer server = await _gameServersStore.GetAsync(gameName, cancellationToken).ConfigureAwait(false); if (server == null) { _log.LogDebug("Server for game {Game} not found", gameName); await SendServerNotFoundAsync(context.Channel, gameName, cancellationToken).ConfigureAwait(false); return; } // check permissions if (!await IsAuthorizedAsync(context, server).ConfigureAwait(false)) { _log.LogTrace("User {UserID} not authorized for server for game {Game} not found", context.User.Id, gameName); await SendUnatuthorizedAsync(context.Channel, server, cancellationToken).ConfigureAwait(false); return; } // build server info embed EmbedBuilder embed = new EmbedBuilder(); embed.Title = $"{server.Game} Server Info"; embed.Color = Color.Blue; StringBuilder builder = new StringBuilder(); if (!string.IsNullOrWhiteSpace(server.RulesURL)) { builder.AppendFormat("Before connecting, please read the [server rules]({0}).\n\n", server.RulesURL); } builder.AppendFormat("***Address***: `{0}`\n", server.Address); if (!string.IsNullOrWhiteSpace(server.Password)) { builder.AppendFormat("***Password***: `{0}`\n", server.Password); } embed.Description = builder.ToString(); // send response - directly if PM, or direct to user's PM if in guild string text = this.IsAutoRemoving ? GetAutoremoveText() : null; IUserMessage sentMsg; if (context.IsPrivate) { sentMsg = await context.ReplyAsync(text, false, embed.Build(), cancellationToken).ConfigureAwait(false); } else { _ = context.InlineReplyAsync($"{_einherjiOptions.SuccessSymbol} I will send you a private message with info on how to connect to the server!"); Task <IUserMessage> pmTask = context.User.SendMessageAsync(text, false, embed.Build(), new RequestOptions { CancelToken = cancellationToken }); sentMsg = await pmTask.ConfigureAwait(false); } // auto remove if (this.IsAutoRemoving) { RemoveMessagesDelayed(_gameServersOptions.AutoRemoveDelay, cancellationToken, sentMsg); } }
private async Task CmdUpdateAccountAsync(SocketCommandContext context, Match match, CancellationToken cancellationToken = default) { using IDisposable logScope = _log.BeginCommandScope(context, this); _log.LogDebug("Updating Netlix account credentials"); if (context.IsPrivate) { _log.LogTrace("Aborting Netflix account credentials updating: Group only"); await SendErrorAsync($"{_einherjiOptions.FailureSymbol} You can't do this in private message.\nGo to {GetAllowedChannelsMentionsText()}.", context.Channel).ConfigureAwait(false); return; } SocketGuildUser user = await context.Guild.GetGuildUserAsync(context.User).ConfigureAwait(false); if (!_netflixAccountOptions.CanModify(user)) { _log.LogTrace("Aborting Netflix account credentials updating: User not privileged"); await SendErrorAsync($"{_einherjiOptions.FailureSymbol} You need {GetAllowedRolesMentionsText()} role to do this.", context.Channel).ConfigureAwait(false); return; } if (!_netflixAccountOptions.IsChannelAllowed(context.Channel)) { _log.LogTrace("Aborting Netflix account credentials updating: Wrong channel"); await SendErrorAsync($"You can't do this here.\nGo to {GetAllowedChannelsMentionsText()}.", context.Channel).ConfigureAwait(false); return; } // retrieve info from store NetflixAccount account = await _netflixAccountStore.GetAsync(cancellationToken).ConfigureAwait(false); // update and save SetMode mode = StringToSetMode(match.Groups[1].Value); string value = match.Groups[2].Value; string responseText = null; if (mode == SetMode.Login) { account.SetLogin(value, context.User.Id); responseText = $"{_einherjiOptions.SuccessSymbol} You have set Netflix account login to `{value}`."; } if (mode == SetMode.Password) { account.SetPassword(value, context.User.Id); responseText = $"{_einherjiOptions.SuccessSymbol} You have set Netflix account password to `{value}`."; } await _netflixAccountStore.SetAsync(account, cancellationToken).ConfigureAwait(false); // create message EmbedBuilder embed = CreateConfirmationEmbed(account, context.User); embed.WithDescription(responseText); string text = this.IsAutoRemoving ? GetAutoremoveText() : null; RestUserMessage sentMsg = await context.ReplyAsync(text, false, embed.Build(), cancellationToken).ConfigureAwait(false); // auto remove if (this.IsAutoRemoving) { RemoveMessagesDelayed(_netflixAccountOptions.AutoRemoveDelay, cancellationToken, sentMsg, context.Message); } }
private async Task CmdInstanceInfoAsync(SocketCommandContext context, Match match, CancellationToken cancellationToken = default) { using IDisposable logScope = _log.BeginCommandScope(context, this); PiholeOptions piholeOptions = _piholeOptions.CurrentValue; EinherjiOptions einherjiOptions = _einherjiOptions.CurrentValue; string instanceID = match.Groups[1].Value; // check instance exists if (!piholeOptions.Instances.TryGetValue(instanceID, out PiholeInstanceOptions instance)) { await context.ReplyAsync($"{einherjiOptions.FailureSymbol} Unknown PiHole instance `{instanceID}`.", cancellationToken).ConfigureAwait(false); return; } string instanceName = string.IsNullOrWhiteSpace(instance.DisplayName) ? instanceID : instance.DisplayName; // check user is authorized to manage that instance if (!instance.IsAuthorized(context.User)) { await context.ReplyAsync($"{einherjiOptions.FailureSymbol} You have no permissions to manage PiHole instance `{instanceName}`.", cancellationToken).ConfigureAwait(false); return; } // send notification to user that we're working on it RestUserMessage workingNotification = await context.ReplyAsync("Querying pihole API, please wait...", cancellationToken).ConfigureAwait(false); EmbedBuilder embed = new EmbedBuilder() .WithThumbnailUrl("https://upload.wikimedia.org/wikipedia/en/thumb/1/15/Pi-hole_vector_logo.svg/1200px-Pi-hole_vector_logo.svg.png") .WithDescription($"Information on **{instanceName}** Kathara PiHole instance"); // add stored instance config if (!instance.HideURL) { embed.AddField("PiHole Address", instance.PiholeURL, false); } if (instance.AuthorizedUserIDs.Count > 0) { embed.AddField("Authorized users", string.Join(", ", instance.AuthorizedUserIDs.Select(uid => MentionUtils.MentionUser(uid))), true); } if (instance.AuthorizedRoleIDs.Count > 0) { embed.AddField("Authorized roles", string.Join(", ", instance.AuthorizedRoleIDs.Select(rid => MentionUtils.MentionRole(rid))), true); } if (instance.AuthorizedUserIDs.Count + instance.AuthorizedRoleIDs.Count == 0) { embed.AddField("Authorized users", "-", true); } // communicate with pihole API _log.LogDebug("Requesting status from Pihole API at {URL} (Instance: {Instance})", instance.PiholeURL, instanceName); HttpClient client = _httpClientFactory.CreateClient(); using (HttpResponseMessage response = await client.GetAsync($"{instance.PiholeURL}/admin/api.php?summary", cancellationToken).ConfigureAwait(false)) { if (!response.IsSuccessStatusCode) { embed.WithColor(einherjiOptions.EmbedErrorColor); await context.ReplyAsync($"{einherjiOptions.FailureSymbol} Request to PiHole API failed: {response.ReasonPhrase} ({response.StatusCode})", false, embed.Build(), cancellationToken).ConfigureAwait(false); await workingNotification.DeleteAsync(cancellationToken).ConfigureAwait(false); return; } string responseContentRaw = await response.Content.ReadAsStringAsync().ConfigureAwait(false); if (!TryParseJObject(responseContentRaw, out JObject responseContentJson)) { embed.WithColor(einherjiOptions.EmbedErrorColor); await context.ReplyAsync($"{einherjiOptions.FailureSymbol} Failed to query PiHole. Please refer to bot logs.", false, embed.Build(), cancellationToken).ConfigureAwait(false); await workingNotification.DeleteAsync(cancellationToken).ConfigureAwait(false); _log.LogError("Failed querying PiHole instance {InstanceID}: {ResponseMessage}", instanceName, responseContentRaw); return; } // deserialize response JsonSerializer serializer = JsonSerializer.CreateDefault(); serializer.Culture = CultureInfo.InvariantCulture; PiholeApiStatusResponse data = responseContentJson.ToObject <PiholeApiStatusResponse>(serializer); // put into embed embed.AddField("Status", data.IsEnabled ? $"{einherjiOptions.SuccessSymbol} Enabled" : $"{einherjiOptions.FailureSymbol} Disabled", false); embed.WithColor(data.IsEnabled ? einherjiOptions.EmbedSuccessColor : einherjiOptions.EmbedErrorColor); embed.AddField("Domains on block list", data.DomainsBeingBlocked.ToString("N0"), true); embed.AddField("DNS queries today", data.DnsQueriesToday.ToString("N0"), true); embed.AddField("Ads blocked today", $"{data.AdsBlockedToday:N0} ({data.AdsPercentageToday}%)", true); embed.AddField("Unique clients", $"{data.UniqueRecentClients:N0} recently, {data.ClientsEverSeen:N0} all-time", true); embed.WithFooter("Gravity last updated"); embed.WithTimestamp(data.GravityLastUpdated); } using (HttpResponseMessage response = await client.GetAsync($"{instance.PiholeURL}/admin/api.php?version")) { if (response.IsSuccessStatusCode && TryParseJObject(await response.Content.ReadAsStringAsync(), out JObject responseContentJson) && !string.IsNullOrWhiteSpace(responseContentJson["version"]?.ToString())) { embed.AddField("PiHole version", responseContentJson["version"].ToString()); } } await context.ReplyAsync(null, false, embed.Build(), cancellationToken).ConfigureAwait(false); await workingNotification.DeleteAsync(cancellationToken).ConfigureAwait(false); }
private async Task CmdMoveAllAsync(SocketCommandContext context, Match match, CancellationToken cancellationToken = default) { using IDisposable logScope = _log.BeginCommandScope(context, this); EinherjiOptions options = _einherjiOptions.CurrentValue; // verify it's a guild message SocketTextChannel channel = await this.VerifyGuildChannelAsync(context, cancellationToken).ConfigureAwait(false); if (channel == null) { return; } // verify command has proper arguments if (match.Groups.Count < 3) { string prefix = _commandsOptions.CurrentValue.Prefix; await context.ReplyAsync($"{options.FailureSymbol} Please specify __both__ channels IDs.\n***{_commandsOptions.CurrentValue.Prefix}move all from <original channel ID> to <target channel ID>***", cancellationToken).ConfigureAwait(false); return; } // verify channels exist // we can do both at once, it's okay if user gets warn about both at once, and it just simplifies the code SocketVoiceChannel channelFrom = await VerifyValidVoiceChannelAsync(match.Groups[1], context.Guild, context.Channel, cancellationToken).ConfigureAwait(false); SocketVoiceChannel channelTo = await VerifyValidVoiceChannelAsync(match.Groups[2], context.Guild, context.Channel, cancellationToken).ConfigureAwait(false); if (channelFrom == null || channelTo == null) { return; } // verify user can see both channels, and has move permission in both _log.LogTrace("Verifying user {ID} has permission to move users between channels {ChannelFromName} ({ChannelFromID}) and {ChannelToName} ({ChannelToID})", context.User.Id, channelFrom.Name, channelFrom.Id, channelTo.Name, channelTo.Id); if (!await this.VerifyUserPermissionsAsync(context, channelFrom, context.User.Id, ChannelPermission.MoveMembers, cancellationToken).ConfigureAwait(false) || !await this.VerifyUserPermissionsAsync(context, channelTo, context.User.Id, ChannelPermission.MoveMembers, cancellationToken).ConfigureAwait(false)) { return; } // verify bot also has permissions _log.LogTrace("Verifying the bot has permission to move users between channels {ChannelFromName} ({ChannelFromID}) and {ChannelToName} ({ChannelToID})", channelFrom.Name, channelFrom.Id, channelTo.Name, channelTo.Id); if (!await this.VerifyUserPermissionsAsync(context, channelFrom, context.Client.CurrentUser.Id, ChannelPermission.MoveMembers, cancellationToken).ConfigureAwait(false) || !await this.VerifyUserPermissionsAsync(context, channelTo, context.Client.CurrentUser.Id, ChannelPermission.MoveMembers, cancellationToken).ConfigureAwait(false)) { return; } // move the users SocketGuildUser[] users = channelFrom.Users.ToArray(); string channelFromMention = GetVoiceChannelMention(channelFrom); string channelToMention = GetVoiceChannelMention(channelTo); _log.LogDebug("Moving {Count} users from channel {ChannelFromName} ({ChannelFromID}) to {ChannelToName} ({ChannelToID})", users.Length, channelFrom.Name, channelFrom.Id, channelTo.Name, channelTo.Id); RestUserMessage response = await context.ReplyAsync($"Moving {users.Length} user{(users.Length > 1 ? "s" : null)} from {channelFromMention} to {channelToMention}.", cancellationToken).ConfigureAwait(false); int errorCount = 0; foreach (SocketGuildUser user in users) { try { await user.ModifyAsync(props => props.Channel = channelTo, cancellationToken).ConfigureAwait(false); } catch { errorCount++; } } // display confirmation StringBuilder builder = new StringBuilder(); int successCount = users.Length - errorCount; builder.AppendFormat("{0} user{3} moved from {1} to {2}.", successCount.ToString(), channelFromMention, channelToMention, successCount > 1 || successCount == 0 ? "s" : null); if (errorCount > 0) { builder.AppendFormat("\nFailed to move {0} user{2}. {1}", errorCount.ToString(), options.FailureSymbol, errorCount > 1 ? "s" : null); } await response.ModifyAsync(props => props.Content = builder.ToString(), cancellationToken).ConfigureAwait(false); }
private async Task CmdDisableAsync(SocketCommandContext context, Match match, CancellationToken cancellationToken = default) { using IDisposable logScope = _log.BeginCommandScope(context, this); PiholeOptions piholeOptions = _piholeOptions.CurrentValue; EinherjiOptions einherjiOptions = _einherjiOptions.CurrentValue; string instanceID = match.Groups[1].Value; // check instance exists if (!piholeOptions.Instances.TryGetValue(instanceID, out PiholeInstanceOptions instance)) { await context.ReplyAsync($"{einherjiOptions.FailureSymbol} Unknown PiHole instance `{instanceID}`.", cancellationToken).ConfigureAwait(false); return; } string instanceName = string.IsNullOrWhiteSpace(instance.DisplayName) ? instanceID : instance.DisplayName; // check user is authorized to access that instance if (!instance.IsAuthorized(context.User)) { await context.ReplyAsync($"{einherjiOptions.FailureSymbol} You have no permissions to access PiHole instance `{instanceName}`.", cancellationToken).ConfigureAwait(false); return; } // parse disable time, defaulting to 5 mins TimeSpan disableTime = piholeOptions.DefaultDisableTime; if (uint.TryParse(match.Groups[2]?.Value, out uint disableMinutes)) { disableTime = TimeSpan.FromMinutes(disableMinutes); } if (disableTime <= TimeSpan.FromMinutes(1)) { await context.ReplyAsync($"{einherjiOptions.FailureSymbol} Minimum disable time is 1 minute.", cancellationToken).ConfigureAwait(false); return; } // send notification to user that we're working on it RestUserMessage workingNotification = await context.ReplyAsync("Querying pihole API, please wait...", cancellationToken).ConfigureAwait(false); // communicate with pihole API _log.LogDebug("Disabling Pihole through API at {URL} for {Duration} (Instance: {Instance})", instance.PiholeURL, instanceName, disableTime); HttpClient client = _httpClientFactory.CreateClient(); using HttpResponseMessage response = await client.GetAsync($"{instance.PiholeURL}/admin/api.php?disable={disableMinutes * 60}&auth={instance.AuthToken}", cancellationToken).ConfigureAwait(false); if (!response.IsSuccessStatusCode) { await context.ReplyAsync($"{einherjiOptions.FailureSymbol} Request to PiHole API failed: {response.ReasonPhrase} ({response.StatusCode})", cancellationToken).ConfigureAwait(false); await workingNotification.DeleteAsync(cancellationToken).ConfigureAwait(false); return; } string responseContentRaw = await response.Content.ReadAsStringAsync().ConfigureAwait(false); if (!TryParseJObject(responseContentRaw, out JObject responseContentJson)) { await context.ReplyAsync($"{einherjiOptions.FailureSymbol} Failed to disable PiHole. Please refer to bot logs.", cancellationToken).ConfigureAwait(false); await workingNotification.DeleteAsync(cancellationToken).ConfigureAwait(false); _log.LogError("Failed disabling PiHole instance {InstanceID}: {ResponseMessage}", instanceName, responseContentRaw); return; } if (!string.Equals(responseContentJson["status"]?.ToString(), "disabled", StringComparison.OrdinalIgnoreCase)) { await context.ReplyAsync($"{einherjiOptions.FailureSymbol} Failed to disable PiHole. Please refer to bot logs.", cancellationToken).ConfigureAwait(false); await workingNotification.DeleteAsync(cancellationToken).ConfigureAwait(false); _log.LogError("Failed disabling PiHole instance {InstanceID}: 'status' is not 'disabled'", instanceName); return; } await context.ReplyAsync($"{einherjiOptions.FailureSymbol} PiHole instance `{instanceName}` has been disabled for {disableTime.TotalMinutes} minutes.", cancellationToken).ConfigureAwait(false); await workingNotification.DeleteAsync(cancellationToken).ConfigureAwait(false); }
private async Task handler(SocketMessage arg) { if (!_isReady) { return; } var msg = arg as SocketUserMessage; if (msg == null) { return; } #if BETA if (!_ownerIds.Contains(msg.Author.Id)) { return; } #endif if (msg.Author.IsBot) { return; } int argPos = 0; if (!msg.HasStringPrefix(_prefix, ref argPos)) { return; } var context = new SocketCommandContext(_client, msg); var result = await _commandService.ExecuteAsync(context, argPos, _dependencyMap, MultiMatchHandling.Best); _logger.Info($"Command ran by {context.User} in {context.Channel.Name} - {context.Message.Content}"); if (result.IsSuccess) { return; } string response = null; switch (result) { case SearchResult searchResult: // "Commnd not found" messages are frowned upon, but if you know your usage environment, this is where you would impliment the logic break; case ParseResult parseResult: response = $":warning: There was an error parsing your command: `{parseResult.ErrorReason}`"; break; case PreconditionResult preconditionResult: response = $":warning: A precondition of your command failed: `{preconditionResult.ErrorReason}`"; break; case ExecuteResult executeResult: response = $":warning: Your command failed to execute. If this persists, contact the Bot Developer.\n`{executeResult.Exception.Message}`"; _logger.Error(executeResult.Exception); break; } if (response != null) { await context.ReplyAsync(response); } }
public async Task BattleAsync(SocketCommandContext context, User userData, Enemy enemyData, DbService db) { var zone = await db.Zones.FirstOrDefaultAsync(x => x.Id == userData.ZoneId); var user = await BuildCombatUserAsync(context.User, userData, db); int lvl; if (userData.Level + 3 > zone.HighLevel) { lvl = zone.HighLevel; } else { lvl = userData.Level + 3; } var enemy = await BuildCombatUserAsync(enemyData, _random.Next(zone.LowLevel, lvl), db); var msgLog = new LinkedList <string>(); msgLog.AddFirst($"{user.Name} VS {enemy.Name}"); var Sendembed = new EmbedBuilder { Description = msgLog.ListToString(), Color = Color.Purple, Fields = new List <EmbedFieldBuilder> { new EmbedFieldBuilder { Name = "Your HP", Value = $"{user.Health - user.DmgTaken}/{user.Health}", IsInline = true }, new EmbedFieldBuilder { Name = "Enemy HP", Value = $"{enemy.Health - enemy.DmgTaken}/{enemy.Health}", IsInline = true } } }; var msg = await context.ReplyAsync(Sendembed); var inventory = await db.Inventories.Where(x => x.UserId == context.User.Id).ToListAsync(); var winner = await CombatAsync(msg, user, enemy, db, msgLog, inventory); if (winner.Name == enemy.Name) { return; } var exp = _level.AddExpAndCredit(enemyData.Exp + _random.Next(0, enemyData.Credit), enemyData.Credit, userData, out var response); var embed = msg.Embeds.First().ToEmbedBuilder(); var update = false; if (exp) { update = true; embed.AddField("Exp", response, true); } if (enemyData.LootTableIds.Length != 0) { update = true; var loot = new List <Item>(); var lootResponse = ""; if (enemyData.Credit != 0) { lootResponse += $"{enemyData.Credit} credit\n"; } var amount = enemyData.LootTableIds.Length > 2 ? 3 : enemyData.LootTableIds.Length; for (var i = 0; i < amount; i++) { var item = await db.Items.FindAsync( enemyData.LootTableIds.ElementAt(_random.Next(enemyData.LootTableIds.Length))); if (!loot.Contains(item)) { loot.Add(item); lootResponse += $"{item.Name}\n"; var invItem = inventory.FirstOrDefault(x => x.ItemId == item.Id); if (invItem != null) { if (!item.Unique) { invItem.Amount++; } } else { await db.Inventories.AddAsync(new Inventory { UserId = context.User.Id, ItemId = item.Id, Amount = 1, ItemType = item.ItemType }); } } else { i--; } } embed.AddField("Loot", lootResponse, true); } if (update) { await msg.ModifyAsync(x => x.Embed = embed.Build()); } await db.SaveChangesAsync(); }
private async Task CmdPurgeAsync(SocketCommandContext message, Match match, CancellationToken cancellationToken = default) { using IDisposable logScope = _log.BeginCommandScope(message, this); EinherjiOptions options = _einherjiOptions.CurrentValue; if (!(message.Channel is SocketTextChannel channel)) { await message.ReplyAsync($"{options.FailureSymbol} Sir, this command is only applicable in guild channels.", cancellationToken).ConfigureAwait(false); return; } SocketGuildUser user = await message.Guild.GetGuildUserAsync(message.User).ConfigureAwait(false); if (!user.GetPermissions(channel).ManageMessages) { await channel.SendMessageAsync($"{options.FailureSymbol} You can't order me to do that.", cancellationToken).ConfigureAwait(false); return; } SocketGuildUser botUser = await message.Guild.GetGuildUserAsync(message.Client.CurrentUser.Id).ConfigureAwait(false); if (!botUser.GetPermissions(channel).ManageMessages) { await channel.SendMessageAsync($"{options.FailureSymbol} I am missing *Manage Messages* permission in this channel.", cancellationToken).ConfigureAwait(false); return; } if (match.Groups.Count == 1 || match.Groups[1]?.Length < 1) { await channel.SendMessageAsync($"{options.FailureSymbol} Sir, I need a positive number of messages to take down.", cancellationToken).ConfigureAwait(false); return; } string countString = match.Groups[1].Value; if (!int.TryParse(countString, out int count)) { await channel.SendMessageAsync($"{options.FailureSymbol} Sir, `{countString} is not a valid number.", cancellationToken).ConfigureAwait(false); return; } if (count < 0) { await channel.SendMessageAsync($"{options.FailureSymbol} Sir, how am I supposed to execute removal of {count} messages?.", cancellationToken).ConfigureAwait(false); return; } // start a new task to prevent deletion from blocking gateway task _ = Task.Run(async() => { try { // get last X messages IEnumerable <IMessage> msgs = await channel.GetMessagesAsync(count + 1, cancellationToken).FlattenAsync().ConfigureAwait(false); RestUserMessage confirmationMsg = null; // bulk can only delete messages not older than 2 weeks DateTimeOffset bulkMaxAge = DateTimeOffset.UtcNow - TimeSpan.FromDays(14) - TimeSpan.FromSeconds(2); IEnumerable <IMessage> newerMessages = msgs.Where(msg => msg.Timestamp >= bulkMaxAge); IEnumerable <IMessage> olderMessages = msgs.Except(newerMessages); int olderCount = olderMessages.Count(); int actualCount = msgs.Count() - 1; _log.LogDebug("Removing {TotalCount} messages. {BulkIncompatibleCount} messages cannot be removed in bulk", msgs.Count(), olderCount); // first delete bulk-deletable await channel.DeleteMessagesAsync(newerMessages, cancellationToken).ConfigureAwait(false); // delete older msgs one by one if (olderCount > 0) { await SendOrUpdateConfirmationAsync($"You are requesting deletion of {actualCount} messages, {olderCount} of which are older than 2 weeks.\n" + "Deleting these messages may take a while due to Discord's rate limiting, so please be patient.").ConfigureAwait(false); foreach (IMessage msg in olderMessages) { await Task.Delay(1500).ConfigureAwait(false); await channel.DeleteMessageAsync(msg, cancellationToken).ConfigureAwait(false); } } await SendOrUpdateConfirmationAsync(actualCount > 0 ? $"{options.SuccessSymbol} Sir, your message and {actualCount} previous message{(actualCount > 1 ? "s were" : " was")} taken down." : $"{options.SuccessSymbol} Sir, I deleted your message. Specify count greater than 0 to remove more than just that.").ConfigureAwait(false); await Task.Delay(6 * 1000, cancellationToken).ConfigureAwait(false); await channel.DeleteMessageAsync(confirmationMsg, cancellationToken).ConfigureAwait(false); async Task SendOrUpdateConfirmationAsync(string text) { if (confirmationMsg == null) { confirmationMsg = await channel.SendMessageAsync(text, cancellationToken).ConfigureAwait(false); } else { await confirmationMsg.ModifyAsync(props => props.Content = text, cancellationToken).ConfigureAwait(false); } } } catch (OperationCanceledException) { } catch (Exception ex) when(ex.LogAsError(_log, "Exception occured when purging messages")) { } }, cancellationToken).ConfigureAwait(false); }