public async Task DeleteAsync(CommandContext ctx, [Description("Channel to delete.")] DiscordChannel channel = null, [RemainingText, Description("Reason.")] string reason = null) { channel = channel ?? ctx.Channel; if (channel.Type == ChannelType.Category && channel.Children.Any()) { if (await ctx.WaitForBoolReplyAsync("The channel specified is a non-empty category and deleting it will delete child channels as well. Continue?")) { foreach (DiscordChannel child in channel.Children.ToList()) { await child.DeleteAsync(reason : ctx.BuildInvocationDetailsString(reason)); } } } else { if (!await ctx.WaitForBoolReplyAsync($"Are you sure you want to delete channel {Formatter.Bold(channel.Name)} (ID: {Formatter.InlineCode(channel.Id.ToString())})? This cannot be undone!")) { return; } } string name = Formatter.Bold(channel.Name); await channel.DeleteAsync(reason : ctx.BuildInvocationDetailsString(reason)); if (channel.Id != ctx.Channel.Id) { await this.InformAsync(ctx, $"Successfully deleted channel: {name}", important : false); } }
private async Task CreateWebhookAsync(CommandContext ctx, DiscordChannel channel, string name, Uri?avatarUrl, string?reason) { // TODO what about other channel types? news, store etc? if (channel?.Type != ChannelType.Text) { throw new InvalidCommandUsageException(ctx, "cmd-err-chn-type-text"); } if (string.IsNullOrWhiteSpace(name) || name.Length > DiscordLimits.NameLimit) { throw new CommandFailedException(ctx, "cmd-err-name", DiscordLimits.NameLimit); } DiscordWebhook wh; if (avatarUrl is null) { wh = await channel.CreateWebhookAsync(name, reason : ctx.BuildInvocationDetailsString(reason)); } else { (_, HttpContentHeaders headers) = await HttpService.HeadAsync(avatarUrl); if (!headers.ContentTypeHeaderIsImage() || headers.ContentLength.GetValueOrDefault() > 8 * 1024 * 1024) { throw new CommandFailedException(ctx, "err-url-image-8mb"); } try { using MemoryStream ms = await HttpService.GetMemoryStreamAsync(avatarUrl); wh = await channel.CreateWebhookAsync(name, ms, reason : ctx.BuildInvocationDetailsString(reason)); } catch (WebException e) { throw new CommandFailedException(ctx, "err-url-image-fail", e); } } if (await ctx.WaitForBoolReplyAsync("q-send-token")) { try { DiscordDmChannel?dm = await ctx.Client.CreateDmChannelAsync(ctx.User.Id); if (dm is { }) { var emb = new LocalizedEmbedBuilder(this.Localization, ctx.Guild.Id); emb.WithLocalizedTitle("fmt-wh-add", Formatter.Bold(Formatter.Strip(wh.Name)), channel.Mention); emb.WithDescription($"||{wh.BuildUrlString()}||"); emb.WithColor(this.ModuleColor); emb.WithThumbnail(wh.AvatarUrl); emb.AddLocalizedTitleField("str-id", wh.Id, inline: true); emb.AddLocalizedTitleField("str-name", wh.Name, inline: true); emb.AddLocalizedTitleField("str-token", $"||{wh.Token}||"); await dm.SendMessageAsync(embed : emb.Build()); } else { await ctx.FailAsync("err-dm-fail"); } } catch {
public async Task SoftBanAsync(CommandContext ctx, [Description("User.")] DiscordMember member, [RemainingText, Description("Reason.")] string reason = null) { if (member.Id == ctx.User.Id) { throw new CommandFailedException("You can't ban yourself."); } await member.BanAsync(delete_message_days : 7, reason : ctx.BuildInvocationDetailsString("(softban) " + reason)); await member.UnbanAsync(ctx.Guild, reason : ctx.BuildInvocationDetailsString("(softban) " + reason)); await this.InformAsync(ctx, $"{Formatter.Bold(ctx.User.Username)} SOFTBANNED {Formatter.Bold(member.Username)}!"); }
public async Task SetChannelTopicAsync(CommandContext ctx, [Description("Reason.")] string reason, [Description("Channel.")] DiscordChannel channel, [RemainingText, Description("New topic.")] string topic) { if (string.IsNullOrWhiteSpace(topic)) { throw new InvalidCommandUsageException("Missing topic."); } if (topic.Length > 1024) { throw new InvalidCommandUsageException("Topic cannot exceed 1024 characters!"); } channel = channel ?? ctx.Channel; await channel.ModifyAsync(new Action <ChannelEditModel>(m => { m.Topic = topic; m.AuditLogReason = ctx.BuildInvocationDetailsString(reason); })); await this.InformAsync(ctx, $"Successfully changed the topic for channel {Formatter.Bold(channel.Name)}", important : false); }
public async Task CreateCategoryAsync(CommandContext ctx, [Description("Channel to clone.")] DiscordChannel channel, [RemainingText, Description("Name for the cloned channel.")] string name = null) { var cloned = await channel.CloneAsync(ctx.BuildInvocationDetailsString()); if (!string.IsNullOrWhiteSpace(name)) { if (name.Length > 100) { throw new InvalidCommandUsageException("Channel name must be shorter than 100 characters."); } if (channel.Type == ChannelType.Text && name.Contains(' ')) { throw new CommandFailedException("Text channel name cannot contain spaces!"); } if (ctx.Guild.Channels.Values.Any(chn => chn.Name == name.ToLowerInvariant())) { if (!await ctx.WaitForBoolReplyAsync("Another channel with that name already exists. Continue?")) { return; } } await cloned.ModifyAsync(new Action <ChannelEditModel>(m => m.Name = name)); } await this.InformAsync(ctx, $"Successfully created clone of channel {Formatter.Bold(channel.Name)} with name {Formatter.Bold(cloned.Name)}.", important : false); }
public async Task GiveRoleAsync(CommandContext ctx, [Description("desc-roles-add")] params DiscordRole[] roles) { SelfRoleService service = ctx.Services.GetRequiredService <SelfRoleService>(); var failedRoles = new List <DiscordRole>(); foreach (DiscordRole role in roles.Distinct()) { if (await service.ContainsAsync(ctx.Guild.Id, role.Id)) { await ctx.Member.GrantRoleAsync(role, ctx.BuildInvocationDetailsString("_gf: Self-granted")); } else { failedRoles.Add(role); } } if (failedRoles.Any()) { await ctx.ImpInfoAsync(this.ModuleColor, "cmd-err-grant-fail", failedRoles.Select(r => r.Mention).JoinWith()); } else { await ctx.InfoAsync(this.ModuleColor); } }
public async Task PruneMembersAsync(CommandContext ctx, [Description("Days.")] int days = 7, [RemainingText, Description("Reason.")] string reason = null) { if (days < 1 || days > 30) { throw new InvalidCommandUsageException("Number of days is not in valid range (max. 30)."); } int count = await ctx.Guild.GetPruneCountAsync(days); if (count == 0) { await this.InformOfFailureAsync(ctx, "No members found to prune..."); return; } if (!await ctx.WaitForBoolReplyAsync($"Pruning will remove {Formatter.Bold(count.ToString())} member(s). Continue?")) { return; } await ctx.Guild.PruneAsync(days, ctx.BuildInvocationDetailsString(reason)); await this.InformAsync(ctx, $"Pruned {Formatter.Bold(count.ToString())} members inactive for {Formatter.Bold(days.ToString())} days", important : false); }
public async Task CreateCategoryAsync(CommandContext ctx, [RemainingText, Description("Name for the category.")] string name) { if (string.IsNullOrWhiteSpace(name)) { throw new InvalidCommandUsageException("Missing category name."); } if (name.Length > 100) { throw new InvalidCommandUsageException("Channel name must be shorter than 100 characters."); } if (ctx.Guild.Channels.Values.Any(chn => chn.Name == name.ToLowerInvariant())) { if (!await ctx.WaitForBoolReplyAsync("A category with that name already exists. Continue?")) { return; } } var c = await ctx.Guild.CreateChannelCategoryAsync(name, reason : ctx.BuildInvocationDetailsString()); await this.InformAsync(ctx, $"Successfully created category: {Formatter.Bold(c.Name)}", important : false); }
public async Task ModifyAsync(CommandContext ctx, [Description("Voice channel to edit")] DiscordChannel channel, [Description("User limit.")] int limit = 0, [Description("Bitrate.")] int bitrate = 0, [RemainingText, Description("Reason.")] string reason = null) { if (channel.Type != ChannelType.Voice) { throw new InvalidCommandUsageException("You can only modify voice channels!"); } if (limit == 0 && bitrate == 0) { throw new InvalidCommandUsageException("You need to specify atleast one change for either bitrate or user limit."); } await channel.ModifyAsync(new Action <ChannelEditModel>(m => { if (limit > 0) { m.Userlimit = limit; } if (bitrate > 0) { m.Bitrate = bitrate; } m.AuditLogReason = ctx.BuildInvocationDetailsString(reason); })).ConfigureAwait(false); await this.InformAsync(ctx, $"Successfully modified voice channel {Formatter.Bold(channel.Name)}:\n\nUser limit: {(limit > 0 ? limit.ToString() : "Not changed")}\nBitrate: {(bitrate > 0 ? bitrate.ToString() : "Not changed")}", important : false); }
public async Task SetRatelimitAsync(CommandContext ctx, [Description("Channel to affect.")] DiscordChannel channel, [Description("New ratelimit.")] int ratelimit, [RemainingText, Description("Reason.")] string reason = null) { if (ratelimit < 0) { throw new InvalidCommandUsageException("Ratelimit value cannot be negative."); } if (!_ratelimitValues.Contains(ratelimit)) { throw new InvalidCommandUsageException($"Ratelimit value must be one of the following: {Formatter.InlineCode(string.Join(", ", _ratelimitValues))}"); } channel = channel ?? ctx.Channel; await channel.ModifyAsync(new Action <ChannelEditModel>(m => { m.PerUserRateLimit = ratelimit; m.AuditLogReason = ctx.BuildInvocationDetailsString(reason); })); await this.InformAsync(ctx, $"Changed the ratemilit setting of channel {Formatter.Bold(channel.Name)} to {Formatter.Bold(ratelimit.ToString())}", important : false); }
public async Task RenameAsync(CommandContext ctx, [Description("Reason.")] string reason, [Description("Channel to rename.")] DiscordChannel channel, [RemainingText, Description("New name.")] string newname) { if (string.IsNullOrWhiteSpace(newname)) { throw new InvalidCommandUsageException("Missing new channel name."); } if (newname.Length < 2 || newname.Length > 100) { throw new InvalidCommandUsageException("Channel name must be longer than 2 and shorter than 100 characters."); } channel = channel ?? ctx.Channel; if (channel.Type == ChannelType.Text && newname.Contains(' ')) { throw new InvalidCommandUsageException("Text channel name cannot contain spaces."); } string name = channel.Name; await channel.ModifyAsync(new Action <ChannelEditModel>(m => { m.Name = newname; m.AuditLogReason = ctx.BuildInvocationDetailsString(reason); })); await this.InformAsync(ctx, $"Successfully renamed channel {Formatter.Bold(name)} to {Formatter.Bold(channel.Name)}", important : false); }
public async Task SetVisibleAsync(CommandContext ctx, [Description("Role.")] DiscordRole role, [Description("Hoisted (visible in online list)?")] bool hoisted = false) { await role.ModifyAsync(hoist : hoisted, reason : ctx.BuildInvocationDetailsString()); await this.InformAsync(ctx, $"Visibility (hoist) var for role {Formatter.Bold(role.Name)} is set to {Formatter.InlineCode(hoisted.ToString())}", important : false); }
public async Task SetMentionableAsync(CommandContext ctx, [Description("Role.")] DiscordRole role, [Description("Mentionable?")] bool mentionable = true) { await role.ModifyAsync(mentionable : mentionable, reason : ctx.BuildInvocationDetailsString()); await this.InformAsync(ctx, $"Mentionable var for role {Formatter.Bold(role.Name)} is set to {Formatter.InlineCode(mentionable.ToString())}", important : false); }
public async Task DeafenAsync(CommandContext ctx, [Description("desc-member")] DiscordMember member, [RemainingText, Description("desc-rsn")] string?reason = null) { await member.SetDeafAsync(true, reason : ctx.BuildInvocationDetailsString(reason)); await ctx.InfoAsync(this.ModuleColor); }
public async Task MuteAsync(CommandContext ctx, [Description("Mute?")] bool mute, [Description("Member to mute.")] DiscordMember member, [RemainingText, Description("Reason.")] string reason = null) { DiscordRole muteRole = await ctx.Services.GetService <RatelimitService>().GetOrCreateMuteRoleAsync(ctx.Guild); if (mute) { await member.GrantRoleAsync(muteRole, ctx.BuildInvocationDetailsString(reason)); } else { await member.RevokeRoleAsync(muteRole, ctx.BuildInvocationDetailsString(reason)); } await this.InformAsync(ctx, $"Successfully {Formatter.Bold(mute ? "muted" : "unmuted")} {member.Mention}", important : false); }
public async Task DeleteAsync(CommandContext ctx, [Description("desc-role")] DiscordRole role, [RemainingText, Description("desc-rsn")] string?reason = null) { await role.DeleteAsync(ctx.BuildInvocationDetailsString(reason)); await ctx.InfoAsync(this.ModuleColor); }
public async Task SetColorAsync(CommandContext ctx, [Description("Role.")] DiscordRole role, [Description("Color.")] DiscordColor color) { await role.ModifyAsync(color : color, reason : ctx.BuildInvocationDetailsString()); await this.InformAsync(ctx, $"Successfully set the color for the role {Formatter.Bold(role.Name)} to {Formatter.InlineCode(role.Color.ToString())}", important : false); }
public async Task MuteAsync(CommandContext ctx, [Description("Mute?")] bool mute, [Description("Member to mute.")] DiscordMember member, [RemainingText, Description("Reason.")] string reason = null) { await member.SetMuteAsync(mute, reason : ctx.BuildInvocationDetailsString(reason)); await this.InformAsync(ctx, $"Successfully {Formatter.Bold(mute ? "muted" : "unmuted")} member {Formatter.Bold(member.DisplayName)}"); }
public async Task DeleteAsync(CommandContext ctx, [Description("Role.")] DiscordRole role, [RemainingText, Description("Reason.")] string reason = null) { string name = Formatter.Bold(role.Name); await role.DeleteAsync(ctx.BuildInvocationDetailsString(reason)); await this.InformAsync(ctx, $"Successfully deleted role: {Formatter.Bold(name)}", important : false); }
public async Task DeafenAsync(CommandContext ctx, [Description("Member.")] DiscordMember member, [Description("Deafen?")] bool deafen, [RemainingText, Description("Reason.")] string reason = null) { await member.SetDeafAsync(deafen, reason : ctx.BuildInvocationDetailsString(reason)); await this.InformAsync(ctx, $"Successfully {Formatter.Bold(deafen ? "deafened" : "undeafened")} member {Formatter.Bold(member.DisplayName)}"); }
public async Task GiveRoleAsync(CommandContext ctx, [Description("Role to grant.")] DiscordRole role) { if (!await this.Database.IsSelfAssignableRoleAsync(ctx.Guild.Id, role.Id)) { throw new CommandFailedException("That role is not in this guild's self-assignable roles list."); } await ctx.Member.GrantRoleAsync(role, ctx.BuildInvocationDetailsString("Granted self-assignable role.")); await this.InformAsync(ctx, "Successfully granted the required roles.", important : false); }
public async Task BanAsync(CommandContext ctx, [Description("Member to ban.")] DiscordMember member, [RemainingText, Description("Reason.")] string reason = null) { if (member.Id == ctx.User.Id) { throw new CommandFailedException("You can't ban yourself."); } await member.BanAsync(delete_message_days : 7, reason : ctx.BuildInvocationDetailsString(reason)); await this.InformAsync(ctx, $"{ctx.Member.Mention} {Formatter.Bold("BANNED")} {member.ToString()}!"); }
public async Task KickAsync(CommandContext ctx, [Description("Member.")] DiscordMember member, [RemainingText, Description("Reason.")] string reason = null) { if (member.Id == ctx.User.Id) { throw new CommandFailedException("You can't kick yourself."); } await member.RemoveAsync(reason : ctx.BuildInvocationDetailsString(reason)); await this.InformAsync(ctx, $"{Formatter.Bold(ctx.User.Username)} kicked {Formatter.Bold(member.Username)}."); }
public async Task UnbanAsync(CommandContext ctx, [Description("User.")] DiscordUser user, [RemainingText, Description("Reason.")] string reason = null) { if (user.Id == ctx.User.Id) { throw new CommandFailedException("You can't unban yourself..."); } await ctx.Guild.UnbanMemberAsync(user.Id, reason : ctx.BuildInvocationDetailsString(reason)); await this.InformAsync(ctx, $"{ctx.Member.Mention} removed a ban for {user.ToString()}!"); }
public async Task UnbanAsync(CommandContext ctx, [Description("ID.")] ulong id, [RemainingText, Description("Reason.")] string reason = null) { if (id == ctx.User.Id) { throw new CommandFailedException("You can't unban yourself..."); } await ctx.Guild.UnbanMemberAsync(id, reason : ctx.BuildInvocationDetailsString(reason)); await this.InformAsync(ctx, $"{ctx.Member.Mention} removed a ban for {Formatter.InlineCode(id.ToString())}!"); }
public async Task RevokeRolesAsync(CommandContext ctx, [Description("Member.")] DiscordMember member, [Description("Role.")] DiscordRole role, [RemainingText, Description("Reason.")] string reason = null) { if (role.Position >= ctx.Member.Hierarchy) { throw new CommandFailedException("You cannot revoke that role."); } await member.RevokeRoleAsync(role, reason : ctx.BuildInvocationDetailsString(reason)); await this.InformAsync(ctx, $"Successfully revoked role {Formatter.Bold(role.Name)} from member {Formatter.Bold(member.DisplayName)}"); }
public async Task RenameAsync(CommandContext ctx, [Description("Role.")] DiscordRole role, [RemainingText, Description("New name.")] string newname) { if (string.IsNullOrWhiteSpace(newname)) { throw new ArgumentException("I need a new name for the role."); } string name = role.Name; await role.ModifyAsync(name : newname, reason : ctx.BuildInvocationDetailsString()); await this.InformAsync(ctx, $"Successfully renamed role {Formatter.Bold(name)} to {Formatter.Bold(role.Name)}", important : false); }
public async Task DeleteMessagesBeforeAsync(CommandContext ctx, [Description("Message before which to delete.")] DiscordMessage message, [Description("Amount.")] int amount = 5, [RemainingText, Description("Reason.")] string reason = null) { if (amount < 1 || amount > 100) { throw new CommandFailedException("Cannot delete less than 1 and more than 100 messages at a time."); } IReadOnlyList <DiscordMessage> msgs = await ctx.Channel.GetMessagesBeforeAsync(message.Id, amount); await ctx.Channel.DeleteMessagesAsync(msgs, ctx.BuildInvocationDetailsString(reason)); }
public async Task DeleteAsync(CommandContext ctx, [Description("Emoji to delete.")] DiscordEmoji emoji) { try { DiscordGuildEmoji gemoji = await ctx.Guild.GetEmojiAsync(emoji.Id); string name = gemoji.Name; await ctx.Guild.DeleteEmojiAsync(gemoji, ctx.BuildInvocationDetailsString()); await this.InformAsync(ctx, $"Successfully deleted emoji: {Formatter.Bold(name)}", important : false); } catch (NotFoundException) { throw new CommandFailedException("Can't find that emoji in list of emoji that I made for this guild."); } }
public async Task DeleteReactionsAsync(CommandContext ctx, [Description("Message.")] DiscordMessage message = null, [RemainingText, Description("Reason.")] string reason = null) { message = message ?? (await ctx.Channel.GetMessagesBeforeAsync(ctx.Channel.LastMessageId, 1)).FirstOrDefault(); if (message == null) { throw new CommandFailedException("Cannot find the specified message."); } await message.DeleteAllReactionsAsync(ctx.BuildInvocationDetailsString(reason)); await this.InformAsync(ctx, important : false); }