public async Task GiveKarma(CommandContext context, [Description("The discord user you'd like to give karma.")] DiscordUser member)
        {
            if (member.Equals(context.Member))
            {
                await context.RespondAsync($"{context.Member.Mention}, you can't give yourself Karma. That would be cheating. You have to earn it from others.");

                return;
            }

            await context.Message.DeleteAsync();

            using IBotAccessProvider provider = this.providerBuilder.Build();

            ulong karmaToAdd = 0;

            for (int i = 0; i < 180; i++)
            {
                karmaToAdd += (ulong)this._rng.Next(1, 4);
            }

            provider.AddKarma(member.Id, context.Guild.Id, karmaToAdd);
            DiscordEmbed response = new DiscordEmbedBuilder()
                                    .WithDescription($"{member.Mention}, you have been bestowed {karmaToAdd} karma.");
            await context.RespondAsync(embed : response);
        }
        public async Task WarnMemberAsync(CommandContext context,
                                          [Description("Guild Member to Warn")]
                                          DiscordMember member,
                                          [Description("Reason for warning")]
                                          [RemainingText]
                                          string reason)
        {
            DiscordEmbedBuilder successEmbed = new DiscordEmbedBuilder()
                                               .WithTitle($"You have received a warning!")
                                               .AddField("Guild:", context.Guild.Name)
                                               .AddField("Reason:", reason);

            using IBotAccessProvider provider = this.providerBuilder.Build();

            DiscordChannel logChannel = await SendModerationEmbedAndGetLogChannel(successEmbed, member, context.Member, context.Guild, provider);

            provider.AddModerationAuditRecord(context.Guild.Id, context.User.Id, member.Id, ModerationActionType.WARN, reason);

            if (logChannel == null)
            {
                return;
            }

            successEmbed = new DiscordEmbedBuilder()
                           .WithTitle($"{member.Username} has received a warning!")
                           .AddField("Moderator:", context.User.Username)
                           .AddField("Reason:", reason)
                           .WithFooter($"{this.clock.GetCurrentInstant():g}");

            await logChannel.SendMessageAsync(embed : successEmbed);

            await context.Message.CreateReactionAsync(DiscordEmoji.FromName(context.Client, ":white_check_mark:"));
        }
        public async Task ShowLeaderBoard(CommandContext context)
        {
            using IBotAccessProvider provider = this.providerBuilder.Build();
            List <GuildKarmaRecord> leaderboardRecords = provider.GetGuildKarmaRecords(context.Guild.Id)
                                                         .OrderByDescending(record => record.CurrentKarma).ToList();
            int requestorRanking = leaderboardRecords.FindIndex(record => record.UserId == context.User.Id) + 1;

            StringBuilder leaderboardBuilder = new StringBuilder()
                                               .AppendLine("```")
                                               .AppendLine($"Your rank: {requestorRanking}")
                                               .AppendLine(new string('-', 20));

            int index = 1;

            foreach (GuildKarmaRecord record in leaderboardRecords)
            {
                try
                {
                    DiscordMember member = await context.Guild.GetMemberAsync(record.UserId);

                    leaderboardBuilder.AppendLine($"{index}. {member.DisplayName} - {record.CurrentKarma} karma");
                    index++;

                    if (index > 10)
                    {
                        break;
                    }
                }
                catch (NotFoundException) { }
            }

            leaderboardBuilder.Append("```");
            await context.RespondAsync(leaderboardBuilder.ToString());
        }
        public async Task ExecuteGroupAsync(CommandContext context)
        {
            using IBotAccessProvider dataAccessProvider = this.botAccessProvider.Build();
            string             prefixString;
            List <GuildPrefix> guildPrefixes = dataAccessProvider
                                               .GetAllAssociatedGuildPrefixes(context.Guild.Id).ToList();

            if (!guildPrefixes.Any())
            {
                prefixString = "^";
            }
            else if (guildPrefixes.Count == 1)
            {
                prefixString = guildPrefixes.First().Prefix;
            }
            else
            {
                prefixString = guildPrefixes
                               .Select(
                    prefix =>
                    prefix.Prefix)
                               .Aggregate(
                    (partial, next)
                    => $"{partial} {next}");
            }

            await context.RespondAsync($"{context.User.Mention}, the prefixes are: {prefixString}");
        }
        public async Task UnscheduleGuildEvent(CommandContext context)
        {
            using IBotAccessProvider provider = this.accessBuilder.Build();
            InteractivityExtension interactivity = context.Client.GetInteractivity();

            DiscordMessage msg = await context.RespondAsync(
                $":wave: Hi, {context.User.Mention}! You want to unschedule an event for your guild?");

            Reaction reaction = await interactivity.AddAndWaitForYesNoReaction(msg, context.User);

            if (reaction != Reaction.Yes)
            {
                return;
            }

            await context.Message.DeleteAsync();

            DateTimeZone memberTimeZone = this.timeZoneProvider[provider.GetUsersTimeZone(context.User.Id).TimeZoneId];

            List <GuildBackgroundJob> guildEventJobs = provider.GetAllAssociatedGuildBackgroundJobs(context.Guild.Id)
                                                       .Where(x => x.GuildJobType == GuildJobType.SCHEDULED_EVENT)
                                                       .OrderBy(x => x.ScheduledTime)
                                                       .ToList();

            DiscordEmbedBuilder removeEventEmbed = new DiscordEmbedBuilder()
                                                   .WithTitle("Select an event to unschedule by typing: <event number>")
                                                   .WithColor(context.Member.Color);

            CustomResult <int> result = await context.WaitForMessageAndPaginateOnMsg(
                GetScheduledEventsPages(guildEventJobs.Select(x => x.WithTimeZoneConvertedTo(memberTimeZone)), interactivity, removeEventEmbed),
                PaginationMessageFunction.CreateWaitForMessageWithIntInRange(context.User, context.Channel, 1, guildEventJobs.Count + 1),
                msg : msg);

            if (result.TimedOut || result.Cancelled)
            {
                DiscordMessage snark = await context.RespondAsync("You never gave me a valid input. Thanks for wasting my time. :triumph:");

                await Task.Delay(5000);

                await context.Channel.DeleteMessagesAsync(new List <DiscordMessage> {
                    msg, snark
                });

                return;
            }

            GuildBackgroundJob job = guildEventJobs[result.Result - 1];

            msg = await msg.ModifyAsync($"{context.User.Mention}, are you sure you want to unschedule this event?", embed : null);

            reaction = await interactivity.AddAndWaitForYesNoReaction(msg, context.User);

            BackgroundJob.Delete(job.HangfireJobId);
            provider.DeleteGuildBackgroundJob(job);
            await msg.DeleteAllReactionsAsync();

            await msg.ModifyAsync("Ok, I've unscheduled that event!", embed : null);
        }
        public async Task ScheduleGuildEvent(
            CommandContext context,
            [Description("The channel to announce the event in")]
            DiscordChannel announcementChannel,
            [Description("The role to announce the event to")]
            DiscordRole role,
            [Description("The date to schedule the event for")]
            [RemainingText]
            string datetimeString
            )
        {
            using IBotAccessProvider provider = this.accessBuilder.Build();

            if (!context.User.TryGetDateTimeZone(provider, this.timeZoneProvider, out DateTimeZone schedulerTimeZone))
            {
                await context.RespondAsync(StringConstants.NoTimeZoneErrorMessage);

                return;
            }

            DiscordMember botMember = await context.Guild.GetMemberAsync(context.Client.CurrentUser.Id);

            if (!announcementChannel.PermissionsFor(botMember).HasPermission(Permissions.SendMessages | Permissions.MentionEveryone))
            {
                await context.RespondAsync($"{context.Member.Mention}, I don't have permission to send messages and mention `@everyone` in that channel.");

                return;
            }

            LocalDateTime datetime = Recognizers.RecognizeDateTime(datetimeString, DateTimeV2Type.DateTime)
                                     .First().Values.Select(value => (LocalDateTime)value.Value).OrderBy(key => key).First();
            DiscordMessage msg = await context.RespondAsync($":wave: Hi, {context.User.Mention}! You want to schedule an event for {datetime:g} in your timezone?");

            InteractivityExtension interactivity = context.Client.GetInteractivity();
            Reaction reaction = await interactivity.AddAndWaitForYesNoReaction(msg, context.User);

            if (reaction != Reaction.Yes)
            {
                return;
            }

            DiscordEmbedBuilder scheduleEmbedBase = new DiscordEmbedBuilder()
                                                    .WithTitle("Select an event by typing: <event number>")
                                                    .WithColor(context.Member.Color);

            GuildEvent selectedEvent = await SelectPredefinedEvent(context, provider, msg, interactivity, scheduleEmbedBase);

            Instant      eventDateTime = datetime.InZoneStrictly(schedulerTimeZone).ToInstant();
            DiscordEmbed embed         = new DiscordEmbedBuilder()
                                         .WithAuthor(context.Member.DisplayName, iconUrl: context.Member.AvatarUrl)
                                         .WithDescription(selectedEvent.EventDesc)
                                         .WithTitle(selectedEvent.EventName)
                                         .Build();
            await msg.ModifyAsync($"You have scheduled the following event for {datetime:g} in your time zone to be output in the {announcementChannel.Mention} channel.", embed : embed);

            this.ScheduleEventsForRole(context, announcementChannel, provider, selectedEvent, eventDateTime, role);
        }
            public async Task SetLogChannel(CommandContext context,
                                            [Description("The channel to set as the log channel for moderation purposes")]
                                            DiscordChannel channel)
            {
                using IBotAccessProvider provider = this._providerBuilder.Build();

                provider.AddOrUpdateGuildLogChannel(context.Guild.Id, channel.Id);

                await channel.SendMessageAsync($"{context.User.Mention}, I have set this channel as the logging channel for this guild.");
            }
 public async Task ShowKarmaOfOtherAsync(CommandContext context, [Description("The member whose karma you'd like to view.")] DiscordMember member)
 {
     using IBotAccessProvider provider = this.providerBuilder.Build();
     GuildKarmaRecord userKarmaRecord = provider.GetUsersGuildKarmaRecord(member.Id, context.Guild.Id);
     DiscordEmbed     response        = new DiscordEmbedBuilder()
                                        .AddField("Karma:", $"{userKarmaRecord.CurrentKarma}", true)
                                        .WithAuthor(member.DisplayName, iconUrl: member.AvatarUrl)
                                        .WithColor(member.Color);
     await context.RespondAsync(embed : response);
 }
        private static DiscordChannel GetGuildLogChannel(DiscordGuild guild, IBotAccessProvider provider)
        {
            GuildLogsChannel guildLogsChannel = provider.GetGuildLogChannel(guild.Id);
            DiscordChannel   logChannel       = null;

            if (guildLogsChannel != null)
            {
                logChannel = guild.GetChannel(guildLogsChannel.ChannelId);
            }

            return(logChannel);
        }
        public async Task RandomEventForRole(CommandContext context,
                                             [Description("Role to mention")]
                                             DiscordRole role)
        {
            DiscordMessage msg = await context.RespondAsync(
                $":wave: Hi, {context.User.Mention}! You want to `@everyone` and announce a random event?");

            await msg.CreateReactionAsync(DiscordEmoji.FromName(context.Client, ":regional_indicator_y:"));

            await msg.CreateReactionAsync(DiscordEmoji.FromName(context.Client, ":regional_indicator_n:"));

            InteractivityExtension interactivity = context.Client.GetInteractivity();

            InteractivityResult <MessageReactionAddEventArgs> interactivityResult =
                await interactivity.WaitForReactionAsync(msg, context.User);

            if (interactivityResult.TimedOut ||
                !interactivityResult.Result.Emoji.Equals(
                    DiscordEmoji.FromName(context.Client, ":regional_indicator_y:")))
            {
                DiscordMessage snark = await context.RespondAsync($"{context.User.Mention}, well then why did you get my attention! Thanks for wasting my time. Now I have to clean up your mess.");

                await Task.Delay(5000);

                await context.Channel.DeleteMessagesAsync(new List <DiscordMessage> {
                    msg, snark, context.Message
                });

                return;
            }

            await context.Channel.DeleteMessagesAsync(new List <DiscordMessage> {
                context.Message, msg
            });

            using IBotAccessProvider provider = this.accessBuilder.Build();
            List <GuildEvent>   guildEvents       = provider.GetAllAssociatedGuildEvents(context.Guild.Id).ToList();
            GuildEvent          selectedEvent     = guildEvents[Random.Next(guildEvents.Count)];
            DiscordEmbedBuilder eventEmbedBuilder = new DiscordEmbedBuilder();

            eventEmbedBuilder
            .WithTitle(selectedEvent.EventName)
            .WithDescription(selectedEvent.EventDesc);
            await context.RespondAsync(role.Mention, embed : eventEmbedBuilder.Build());
        }
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously

        private async Task <int> PrefixResolver(DiscordMessage msg)

        {
            using IBotAccessProvider dataAccessProvider = this.providerBuilder.Build();
            List <GuildPrefix> guildPrefixes = dataAccessProvider.GetAllAssociatedGuildPrefixes(msg.Channel.GuildId).ToList();

            if (!guildPrefixes.Any())
            {
                return(msg.GetStringPrefixLength("^"));
            }

            foreach (int length in guildPrefixes.Select(prefix => msg.GetStringPrefixLength(prefix.Prefix)).Where(length => length != -1))
            {
                return(length);
            }

            return(-1);
        }
        public async Task MuteMemberAsync(CommandContext context,
                                          [Description("The member to mute")]
                                          DiscordMember member,
                                          [RemainingText]
                                          [Description("The reason for the mute")]
                                          string reason = null)
        {
            DiscordRole mutedRole = await GetOrCreateMutedRole(context);

            DiscordEmbedBuilder successEmbed = new DiscordEmbedBuilder()
                                               .WithTitle($"You have been muted in {context.Guild.Name}!");

            if (reason != null)
            {
                successEmbed.AddField("Reason:", reason);
            }

            using IBotAccessProvider provider = this.providerBuilder.Build();

            DiscordChannel logChannel = await SendModerationEmbedAndGetLogChannel(successEmbed, member, context.Member, context.Guild, provider);

            await member.GrantRoleAsync(mutedRole, reason);

            provider.AddModerationAuditRecord(context.Guild.Id, context.User.Id, member.Id, ModerationActionType.MUTE, reason);
            await context.Message.CreateReactionAsync(DiscordEmoji.FromName(context.Client, ":white_check_mark:"));

            if (logChannel == null)
            {
                return;
            }

            successEmbed = new DiscordEmbedBuilder()
                           .WithTitle($"{member.DisplayName} was muted")
                           .AddField("Moderator", context.Member.DisplayName)
                           .WithFooter($"{this.clock.GetCurrentInstant():g}");

            if (reason != null)
            {
                successEmbed.AddField("Reason", reason);
            }

            await logChannel.SendMessageAsync(embed : successEmbed);
        }
Beispiel #13
0
        public async Task RemoveRole(ulong guildId, ulong userId, ulong roleId)
        {
            try
            {
                DiscordClient shardClient  = this._bot.Client.GetShard(guildId);
                DiscordGuild  discordGuild = await shardClient.GetGuildAsync(guildId);

                DiscordMember guildMember = await discordGuild.GetMemberAsync(userId);

                DiscordRole guildRole = discordGuild.GetRole(roleId);
                await guildMember.RevokeRoleAsync(guildRole);

                using IBotAccessProvider provider = this._providerBuilder.Build();
            }
            catch (Exception e)
            {
                this._logger.LogError("Error in revoking role", e);
            }
        }
        public async Task BanMemberAsync(CommandContext context,
                                         [Description("Guild Member to Ban")]
                                         DiscordMember member,
                                         [Description("Number of days worth of their messages to delete")]
                                         int numDays = 0,
                                         [Description("Reason for ban")]
                                         [RemainingText] string reason = null)
        {
            DiscordEmbedBuilder messageEmbed = new DiscordEmbedBuilder()
                                               .WithTitle($"You have been banned from {context.Guild.Name}!");

            if (reason != null)
            {
                messageEmbed.AddField("Reason:", reason);
            }

            using IBotAccessProvider provider = this.providerBuilder.Build();

            DiscordChannel logChannel = await SendModerationEmbedAndGetLogChannel(messageEmbed, member, context.Member, context.Guild, provider);

            await member.BanAsync(numDays, reason);

            provider.AddModerationAuditRecord(context.Guild.Id, context.User.Id, member.Id, ModerationActionType.BAN, reason);

            if (logChannel == null)
            {
                return;
            }

            DiscordEmbedBuilder successEmbed = new DiscordEmbedBuilder()
                                               .WithTitle($"{member.Username} was banned")
                                               .AddField("Moderator", context.User.Username)
                                               .WithFooter($"{this.clock.GetCurrentInstant():g}");

            if (reason != null)
            {
                successEmbed.AddField("Reason:", reason);
            }

            await logChannel.SendMessageAsync(embed : successEmbed);

            await context.Message.CreateReactionAsync(DiscordEmoji.FromName(context.Client, ":white_check_mark:"));
        }
        /// <summary>
        /// Verifies that this DiscordUser has a timezone registered in our database and throws a PreExecutionException if they don't
        /// </summary>
        /// <param name="user">The user who should have a timezone</param>
        /// <param name="provider">The database access provider</param>
        /// <param name="timeZoneProvider"></param>
        /// <returns></returns>
        public static bool TryGetDateTimeZone(this DiscordUser user, IBotAccessProvider provider, IDateTimeZoneProvider timeZoneProvider, out DateTimeZone timeZone)
        {
            UserTimeZone userTimeZone = provider.GetUsersTimeZone(user.Id);

            try
            {
                if (userTimeZone != null)
                {
                    timeZone = timeZoneProvider[userTimeZone.TimeZoneId];
                    return(true);
                }
            }
            catch (DateTimeZoneNotFoundException)
            {
            }

            timeZone = default;
            return(false);
        }
        public async Task RemovePrefix(
            CommandContext context,
            [Description("The specific string prefix to remove from the guild's prefixes.")]
            string prefixToRemove)
        {
            using IBotAccessProvider dataAccessProvider = this.botAccessProvider.Build();
            GuildPrefix guildPrefix = dataAccessProvider.GetAllAssociatedGuildPrefixes(context.Guild.Id)
                                      .FirstOrDefault(e => e.Prefix.Equals(prefixToRemove));

            if (guildPrefix is null)
            {
                await context.RespondAsync(
                    $"{context.User.Mention}, I'm sorry but the prefix you have given me does not exist for this guild.");

                return;
            }

            dataAccessProvider.DeleteGuildPrefix(guildPrefix);
            await context.RespondAsync(
                $"{context.User.Mention}, I have removed the prefix {guildPrefix.Prefix} for this server.");
        }
        public BotService(
            ILoggerFactory loggerFactory,
            IOptions <BotConfig> botConfig,
            IBotAccessProviderBuilder dataAccessProviderBuilder,
            IDateTimeZoneProvider timeZoneProvider,
            IServiceProvider services)
        {
            DiscordConfiguration ClientConfig = new DiscordConfiguration
            {
                Token           = botConfig.Value.BotToken,
                TokenType       = TokenType.Bot,
                MinimumLogLevel = LogLevel.Information,
                LoggerFactory   = loggerFactory,
                Intents         = DiscordIntents.All,
            };

            this.commandsConfig = new CommandsNextConfiguration
            {
                PrefixResolver      = PrefixResolver,
                Services            = services,
                EnableDms           = true,
                EnableMentionPrefix = true,
            };

            this.interactivityConfig = new InteractivityConfiguration
            {
                PaginationBehaviour = PaginationBehaviour.Ignore,
                Timeout             = TimeSpan.FromMinutes(2),
            };


            this.Client           = new DiscordShardedClient(ClientConfig);
            this._devUserId       = botConfig.Value.DevId;
            this.logger           = loggerFactory.CreateLogger("BotService");
            this.providerBuilder  = dataAccessProviderBuilder;
            this.timeZoneProvider = timeZoneProvider;

            using IBotAccessProvider provider = providerBuilder.Build();
            provider.Migrate();
        }
        public async Task UpdateTimeZone(CommandContext context)
        {
            using IBotAccessProvider accessProvider = this.providerBuilder.Build();

            if (accessProvider.GetUsersTimeZone(context.User.Id) == null)
            {
                await context.RespondAsync(
                    $"{context.User.Mention}, you don't have a timezone set up. To initialize your timezone please type `time init`.");

                return;
            }
            await context.RespondAsync(
                "Please navigate to https://kevinnovak.github.io/Time-Zone-Picker/ and select your timezone. After you do please hit the copy button and paste the contents into the chat.");

            InteractivityExtension interactivity        = context.Client.GetInteractivity();
            InteractivityResult <DiscordMessage> result = await interactivity.WaitForMessageAsync(msg => msg.Author.Equals(context.Message.Author));

            if (!result.TimedOut)
            {
                DateTimeZone test = this.timeZoneProvider.GetZoneOrNull(result.Result.Content);
                if (test != null)
                {
                    UserTimeZone updatedUserTimeZone = accessProvider.GetUsersTimeZone(context.Message.Author.Id);
                    updatedUserTimeZone.TimeZoneId = result.Result.Content;
                    accessProvider.UpdateUserTimeZone(updatedUserTimeZone);
                    await context.RespondAsync(
                        $"I updated your timezone to {result.Result.Content} in all guilds I am a member of.");
                }
                else
                {
                    await context.RespondAsync("You provided me with an invalid timezone. Try again by typing `time update`.");
                }
            }
            else
            {
                await context.RespondAsync(
                    "You either waited too long to respond. Try again by typing `time update`.");
            }
        }
        public async Task ExecuteGroupAsync(CommandContext context, [Description("User to request current time for")] DiscordUser member)
        {
            using IBotAccessProvider provider = this.providerBuilder.Build();
            UserTimeZone memberTimeZone = provider.GetUsersTimeZone(member.Id);

            if (memberTimeZone == null)
            {
                await context.RespondAsync("This user doesn't have a timezone set up. Please try again after the mentioned user has set up their timezone using `time init`");

                return;
            }

            DateTimeZone memberDateTimeZone = this.timeZoneProvider[memberTimeZone.TimeZoneId];

            this.clock.GetCurrentInstant().InZone(memberDateTimeZone).Deconstruct(out LocalDateTime localDateTime, out _, out _);
            localDateTime.Deconstruct(out _, out LocalTime localTime);
            DiscordEmbed outputEmbed = new DiscordEmbedBuilder()
                                       .WithAuthor(iconUrl: member.AvatarUrl)
                                       .WithTitle($"{localTime.ToString("t", null)}");

            await context.RespondAsync(embed : outputEmbed);
        }
        public async Task CurrentTimeAsync(CommandContext context)
        {
            using IBotAccessProvider provider = this.providerBuilder.Build();
            UserTimeZone memberTimeZone = provider.GetUsersTimeZone(context.User.Id);

            if (memberTimeZone == null)
            {
                await context.RespondAsync("You don't have a timezone set up. Please try again after using `time init`");

                return;
            }

            DateTimeZone memberDateTimeZone = this.timeZoneProvider[memberTimeZone.TimeZoneId];

            this.clock.GetCurrentInstant().InZone(memberDateTimeZone).Deconstruct(out LocalDateTime localDateTime, out _, out _);
            localDateTime.Deconstruct(out _, out LocalTime localTime);
            DiscordEmbed outputEmbed = new DiscordEmbedBuilder()
                                       .WithAuthor(iconUrl: context.User.AvatarUrl)
                                       .WithTitle($"{localTime.ToString("t", null)}");

            await context.RespondAsync(embed : outputEmbed);
        }
        public async Task AddPrefix(
            CommandContext context,
            [Description("The new prefix that you want to add to the guild's prefixes. Must be at least one character")]
            string newPrefix)
        {
            if (newPrefix.Length < 1)
            {
                await context.RespondAsync("I'm sorry, but any new prefix must be at least one character.");

                return;
            }

            if (newPrefix.Length > 20)
            {
                await context.RespondAsync("I'm sorry, but any new prefix must be less than 20 characters.");

                return;
            }

            using IBotAccessProvider dataAccessProvider = this.botAccessProvider.Build();
            dataAccessProvider.AddGuildPrefix(context.Guild.Id, newPrefix);
            await context.RespondAsync(
                $"Congratulations, you have added the prefix {newPrefix} to your server's prefixes for Handy Hansel.\nJust a reminder, this disables the default prefix for Handy Hansel unless you specifically add that prefix in again later or do not have any prefixes of your own.");
        }
        public async Task ShowAuditLogVersionOne(CommandContext context,
                                                 [Description("The moderator who took action to filter on.")]
                                                 DiscordUser moderator = null,
                                                 [Description("The member who had action taken against them to filter on.")]
                                                 DiscordUser member = null,
                                                 [Description("The kind of action taken to filter on")]
                                                 ModerationActionType action = ModerationActionType.NONE)
        {
            using IBotAccessProvider provider = this.providerBuilder.Build();

            ulong?modUserId  = moderator?.Id;
            ulong?userUserId = member?.Id;

            List <GuildModerationAuditRecord>   auditRecords = provider.GetGuildModerationAuditRecords(context.Guild.Id, modUserId, userUserId, action).ToList();
            IReadOnlyCollection <DiscordMember> memberList   = await context.Guild.GetAllMembersAsync();

            IDictionary <ulong, DiscordMember> memberDict = memberList.ToDictionary(member => member.Id);

            List <Page> auditPages = GenerateAuditPages(auditRecords, memberDict, context.Client.CurrentUser);

            InteractivityExtension interactivity = context.Client.GetInteractivity();

            await interactivity.SendPaginatedMessageAsync(context.Channel, context.User, auditPages, emojis : null, behaviour : PaginationBehaviour.Ignore, PaginationDeletion.DeleteMessage, timeoutoverride : TimeSpan.FromMinutes(5));
        }
        private static async Task <GuildEvent> SelectPredefinedEvent(CommandContext context, IBotAccessProvider provider, DiscordMessage msg, InteractivityExtension interactivity, DiscordEmbedBuilder scheduleEmbedBase)
        {
            List <GuildEvent>  guildEvents = provider.GetAllAssociatedGuildEvents(context.Guild.Id).ToList();
            IEnumerable <Page> pages       = GetGuildEventsPages(guildEvents, interactivity, scheduleEmbedBase);
            CustomResult <int> result      = await context.WaitForMessageAndPaginateOnMsg(pages,
                                                                                          PaginationMessageFunction.CreateWaitForMessageWithIntInRange(context.User, context.Channel, 1, guildEvents.Count + 1),
                                                                                          msg : msg
                                                                                          );

            if (result.TimedOut || result.Cancelled)
            {
                DiscordMessage snark = await context.RespondAsync("You never gave me a valid input. Thanks for wasting my time. :triumph:");

                await Task.Delay(5000);

                await context.Channel.DeleteMessagesAsync(new List <DiscordMessage> {
                    msg, snark
                });

                return(null);
            }

            return(guildEvents[result.Result - 1]);
        }
        public async Task InteractiveRemovePrefix(CommandContext context)
        {
            using IBotAccessProvider provider = this.botAccessProvider.Build();

            List <GuildPrefix> guildPrefixes = provider.GetAllAssociatedGuildPrefixes(context.Guild.Id).ToList();

            if (!guildPrefixes.Any())
            {
                await context.RespondAsync("You don't have any custom prefixes to remove");

                return;
            }

            DiscordMessage msg = await context.RespondAsync(
                $":wave: Hi, {context.User.Mention}! You want to remove a prefix from your guild list?");

            await msg.CreateReactionAsync(DiscordEmoji.FromName(context.Client, ":regional_indicator_y:"));

            await msg.CreateReactionAsync(DiscordEmoji.FromName(context.Client, ":regional_indicator_n:"));

            InteractivityExtension interactivity = context.Client.GetInteractivity();

            InteractivityResult <MessageReactionAddEventArgs> interactivityResult =
                await interactivity.WaitForReactionAsync(msg, context.User);

            if (interactivityResult.TimedOut ||
                !interactivityResult.Result.Emoji.Equals(
                    DiscordEmoji.FromName(context.Client, ":regional_indicator_y:")))
            {
                DiscordMessage snark = await context.RespondAsync("Well then why did you get my attention! Thanks for wasting my time.");

                await Task.Delay(5000);

                await context.Channel.DeleteMessagesAsync(new List <DiscordMessage> {
                    msg, snark
                });

                return;
            }

            DiscordEmbedBuilder removeEventEmbed = new DiscordEmbedBuilder()
                                                   .WithTitle("Select a prefix to remove by typing: <prefix number>")
                                                   .WithColor(context.Member.Color);

            Task <(bool, int)> messageValidationAndReturn(MessageCreateEventArgs messageE)
            {
                if (messageE.Author.Equals(context.User) && int.TryParse(messageE.Message.Content, out int eventToChoose))
                {
                    return(Task.FromResult((true, eventToChoose)));
                }
                else
                {
                    return(Task.FromResult((false, -1)));
                }
            }

            await msg.DeleteAllReactionsAsync();

            CustomResult <int> result = await context.WaitForMessageAndPaginateOnMsg(
                GetGuildPrefixPages(guildPrefixes, interactivity, removeEventEmbed),
                messageValidationAndReturn,
                msg : msg);

            if (result.TimedOut || result.Cancelled)
            {
                DiscordMessage snark = await context.RespondAsync("You never gave me a valid input. Thanks for wasting my time. :triumph:");

                await Task.Delay(5000);

                await context.Channel.DeleteMessagesAsync(new List <DiscordMessage> {
                    msg, snark
                });

                return;
            }

            GuildPrefix selectedPrefix = guildPrefixes[result.Result - 1];

            provider.DeleteGuildPrefix(selectedPrefix);

            await msg.ModifyAsync(
                $"You have deleted the prefix \"{selectedPrefix.Prefix}\" from this guild's prefixes.", embed : null);
        }
        private async Task AddGuildEventInteractive(CommandContext context, InteractivityExtension interactivity, DiscordMessage msg)
        {
            using IBotAccessProvider provider = this.accessBuilder.Build();

            if (msg == null)
            {
                await context.RespondAsync(content : "Ok, what do you want the event name to be?");
            }
            else
            {
                await msg.ModifyAsync(content : "Ok, what do you want the event name to be?");
            }

            InteractivityResult <DiscordMessage> result = await interactivity.WaitForMessageAsync(xm => xm.Author.Equals(context.User) && xm.Channel.Equals(context.Channel));

            if (result.TimedOut)
            {
                DiscordMessage snark = await context.RespondAsync(
                    content : "You failed to provide a valid event title within the time limit, so thanks for wasting a minute of myyyy time. :triumph:");

                await Task.Delay(5000);

                await context.Channel.DeleteMessagesAsync(new List <DiscordMessage> {
                    msg, result.Result, snark
                });

                return;
            }

            string eventName = result.Result.Content;

            await result.Result.DeleteAsync();

            await msg.ModifyAsync("What do you want the event description to be?");

            result = await interactivity.WaitForMessageAsync(xm => xm.Author.Equals(context.User) && xm.Channel.Equals(context.Channel), timeoutoverride : TimeSpan.FromMinutes(3));

            if (result.TimedOut)
            {
                DiscordMessage snark = await context.RespondAsync(
                    content : "You failed to provide a valid event description within the time limit, so thanks for wasting a minute of myyyy time. :triumph:");

                await Task.Delay(5000);

                await context.Channel.DeleteMessagesAsync(new List <DiscordMessage> {
                    msg, result.Result, snark
                });

                return;
            }

            string eventDesc = result.Result.Content;
            await result.Result.DeleteAsync();

            provider.AddGuildEvent(context.Guild.Id, eventName, eventDesc);
            DiscordEmbed embed = new DiscordEmbedBuilder()
                                 .WithAuthor(context.Client.CurrentUser.Username, iconUrl: context.Client.CurrentUser.AvatarUrl)
                                 .WithDescription(eventDesc)
                                 .WithTitle(eventName)
                                 .Build();

            await msg.ModifyAsync("You have added the following event to your guild:", embed : embed);
        }
 public async Task UnsetLogChannelAsync(CommandContext context)
 {
     using IBotAccessProvider provider = this._providerBuilder.Build();
     provider.RemoveGuildLogChannel(context.Guild.Id);
     await context.RespondAsync($"{context.User.Mention}, I have unset the log channel for this guild.");
 }
        private static async Task <DiscordChannel> SendModerationEmbedAndGetLogChannel(DiscordEmbed embed, DiscordMember member, DiscordMember moderator, DiscordGuild guild, IBotAccessProvider provider)
        {
            DiscordChannel logChannel = GetGuildLogChannel(guild, provider);

            try
            {
                await member.SendMessageAsync(embed : embed);
            }
            catch (UnauthorizedException)
            {
                if (logChannel != null)
                {
                    await logChannel.SendMessageAsync("This user has closed their DMs and so I was not able to message the user.");
                }
                else
                {
                    try
                    {
                        await moderator.SendMessageAsync("This user has closed their DMs and so I was not able to message the user.");
                    }
                    catch (UnauthorizedException)
                    {
                    }
                }
            }

            return(logChannel);
        }
        public async Task TempMuteMemberAsync(CommandContext context,
                                              [Description("The member to mute")]
                                              DiscordMember member,
                                              [Description("Duration to mute the member for (must be quoted if there are any spaces, however it should work with colloquial language)")]
                                              string durationOfMute,
                                              [RemainingText]
                                              [Description("The reason for the mute")]
                                              string reason = null)
        {
            DateTimeV2ModelResult durationResult = DateTimeRecognizer
                                                   .RecognizeDateTime(durationOfMute, culture: Culture.English)
                                                   .Select(model => model.ToDateTimeV2ModelResult())
                                                   .Where(result => result.TypeName is DateTimeV2Type.Duration)
                                                   .FirstOrDefault();

            if (durationResult == null)
            {
                await context.RespondAsync("There was an error parsing the duration");

                return;
            }

            Duration duration       = (Duration)durationResult.Values.FirstOrDefault().Value;
            string   durationString = Period.FromSeconds((long)duration.TotalSeconds).AsHumanReadableString();

            DiscordRole mutedRole = await GetOrCreateMutedRole(context);

            DiscordEmbedBuilder successEmbed = new DiscordEmbedBuilder()
                                               .WithTitle($"You have been temporarily muted in {context.Guild.Name}!")
                                               .AddField("Duration", durationString);

            if (reason != null)
            {
                successEmbed.AddField("Reason:", reason);
            }

            using IBotAccessProvider provider = this.providerBuilder.Build();

            DiscordChannel logChannel = await SendModerationEmbedAndGetLogChannel(successEmbed, member, context.Member, context.Guild, provider);

            await member.GrantRoleAsync(mutedRole, reason);

            provider.AddModerationAuditRecord(context.Guild.Id, context.User.Id, member.Id, ModerationActionType.MUTE, reason);
            await context.Message.CreateReactionAsync(DiscordEmoji.FromName(context.Client, ":white_check_mark:"));

            if (logChannel == null)
            {
                return;
            }

            successEmbed = new DiscordEmbedBuilder()
                           .WithTitle($"{member.DisplayName} was muted")
                           .AddField("Moderator", context.Member.DisplayName)
                           .AddField("Duration", durationString)
                           .WithFooter($"{this.clock.GetCurrentInstant():g}");

            if (reason != null)
            {
                successEmbed.AddField("Reason", reason);
            }

            await logChannel.SendMessageAsync(embed : successEmbed);

            string jobId = BackgroundJob.Schedule <ModerationService>(service => service.RemoveRole(context.Guild.Id, member.Id, mutedRole.Id), duration.ToTimeSpan());

            provider.AddGuildBackgroundJob(jobId, context.Guild.Id, $"Unmute - {member.DisplayName}", this.clock.GetCurrentInstant() + duration, GuildJobType.TEMP_MUTE);
        }
        private async Task SendAdjustedDate(DiscordClient c, MessageReactionAddEventArgs e)
        {
            if (e.User.IsBot)
            {
                return;
            }

            DiscordChannel channel = await c.GetChannelAsync(e.Channel.Id);

            _ = Task.Run(async() =>
            {
                if (e.Emoji.Equals(this.clockEmoji))
                {
                    try
                    {
                        using IBotAccessProvider database = this.providerBuilder.Build();
                        DiscordMember reactor             = (DiscordMember)e.User;
                        DiscordMessage msg = await channel.GetMessageAsync(e.Message.Id);
                        IEnumerable <DateTimeV2ModelResult> parserList = DateTimeRecognizer.RecognizeDateTime(msg.Content, culture: Culture.English)
                                                                         .Select(x => x.ToDateTimeV2ModelResult()).Where(x => x.TypeName is DateTimeV2Type.Time or DateTimeV2Type.DateTime);

                        if (!parserList.Any())
                        {
                            await reactor.SendMessageAsync("Hey, you're stupid, stop trying to react to messages that don't have times in them.");
                            return;
                        }

                        DiscordEmbedBuilder reactorTimeEmbed = new DiscordEmbedBuilder().WithTitle("You requested a timezone conversion");

                        string opTimeZoneId      = database.GetUsersTimeZone(msg.Author.Id)?.TimeZoneId;
                        string reactorTimeZoneId = database.GetUsersTimeZone(e.User.Id)?.TimeZoneId;

                        if (opTimeZoneId is null)
                        {
                            await reactor.SendMessageAsync("The original poster has not set up a time zone yet.");
                            return;
                        }

                        if (reactorTimeZoneId is null)
                        {
                            await channel.SendMessageAsync("You have not set up a time zone yet. Use `time init` to set up your time zone.");
                            return;
                        }

                        DateTimeZone opTimeZone      = this.timeZoneProvider.GetZoneOrNull(opTimeZoneId);
                        DateTimeZone reactorTimeZone = this.timeZoneProvider.GetZoneOrNull(reactorTimeZoneId);
                        if (opTimeZone == null || reactorTimeZone == null)
                        {
                            await reactor.SendMessageAsync("There was a problem, please reach out to your bot developer.");
                            return;
                        }

                        IEnumerable <(string, DateTimeV2Value)> results = parserList.SelectMany(x => x.Values.Select(y => (x.Text, y)));
                        foreach ((string parsedText, DateTimeV2Value result) in results)
                        {
                            string outputString;
                            if (result.Type is DateTimeV2Type.Time)
                            {
                                DateTimeOffset messageDateTime     = msg.Timestamp;
                                ZonedDateTime zonedMessageDateTime = ZonedDateTime.FromDateTimeOffset(messageDateTime);
                                LocalTime localParsedTime          = (LocalTime)result.Value;
                                LocalDateTime localParsedDateTime  = localParsedTime.On(zonedMessageDateTime.LocalDateTime.Date);
                                ZonedDateTime zonedOpDateTime      = localParsedDateTime.InZoneStrictly(opTimeZone);
                                ZonedDateTime zonedReactorDateTime = zonedOpDateTime.WithZone(reactorTimeZone);
                                outputString = zonedReactorDateTime.LocalDateTime.TimeOfDay.ToString("t", null);
                            }
                            else
                            {
                                LocalDateTime localParsedDateTime  = (LocalDateTime)result.Value;
                                ZonedDateTime zonedOpDateTime      = localParsedDateTime.InZoneStrictly(opTimeZone);
                                ZonedDateTime zonedReactorDateTime = zonedOpDateTime.WithZone(reactorTimeZone);
                                outputString = zonedReactorDateTime.LocalDateTime.ToString("g", null);
                            }

                            reactorTimeEmbed
                            .AddField("Poster's Time", $"\"{parsedText}\"")
                            .AddField("Your time", $"{outputString}");
                        }
                        await reactor.SendMessageAsync(embed: reactorTimeEmbed);
                    }
                    catch (Exception exception)
                    {
                        this.logger.Log(LogLevel.Error, exception, "Error in sending reactor the DM");
                    }
                }
            });
        }
        private void ScheduleEventsForRole(CommandContext context, DiscordChannel announcementChannel, IBotAccessProvider provider, GuildEvent selectedEvent, Instant eventDateTime, DiscordRole role)
        {
            Duration eventScheduleDuration = eventDateTime - this.clock.GetCurrentInstant();
            string   scheduledJobId        = BackgroundJob.Schedule <EventService>(
                eventService =>
                eventService.SendEmbedWithMessageToChannelAsUser(
                    context.Guild.Id,
                    context.Member.Id,
                    announcementChannel.Id,
                    $"{(role == null ? "@everyone" : role.Mention)}, this event is starting now!",
                    selectedEvent.EventName,
                    selectedEvent.EventDesc),
                eventScheduleDuration.ToTimeSpan());

            provider.AddGuildBackgroundJob(scheduledJobId, context.Guild.Id, $"{selectedEvent.EventName} - Announcement", eventDateTime, GuildJobType.SCHEDULED_EVENT);

            scheduledJobId = BackgroundJob.Schedule <EventService>(
                eventService
                => eventService.SendEmbedWithMessageToChannelAsUser(
                    context.Guild.Id,
                    context.Member.Id,
                    announcementChannel.Id,
                    $"{(role == null ? "@everyone" : role.Mention)}, this event is starting in 10 minutes!",
                    selectedEvent.EventName,
                    selectedEvent.EventDesc
                    ),
                (eventScheduleDuration - Duration.FromMinutes(10)).ToTimeSpan());

            provider.AddGuildBackgroundJob(scheduledJobId, context.Guild.Id, $"{selectedEvent.EventName} - 10 Min Announcement", eventDateTime - Duration.FromMinutes(10), GuildJobType.SCHEDULED_EVENT);
        }