Example #1
0
        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 {
Example #3
0
        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)}!");
        }
Example #4
0
        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);
        }
Example #5
0
        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);
            }
        }
Example #7
0
        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);
        }
Example #8
0
        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);
        }
Example #9
0
        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);
        }
Example #10
0
        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);
        }
Example #11
0
        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);
        }
Example #12
0
        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);
        }
Example #13
0
        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);
        }
Example #14
0
        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);
        }
Example #15
0
        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);
        }
Example #16
0
        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);
        }
Example #17
0
        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);
        }
Example #18
0
        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)}");
        }
Example #19
0
        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);
        }
Example #20
0
        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)}");
        }
Example #21
0
        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);
        }
Example #22
0
        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()}!");
        }
Example #23
0
        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)}.");
        }
Example #24
0
        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()}!");
        }
Example #25
0
        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())}!");
        }
Example #26
0
        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)}");
        }
Example #27
0
        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);
        }
Example #28
0
            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));
            }
Example #29
0
        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.");
            }
        }
Example #30
0
            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);
            }