コード例 #1
0
        static async Task NotifyActionLogsMentionsFound(Dictionary <ulong, List <DiscordMessage> > warnDict, DiscordChannel channel)
        {
            foreach (ulong snowflake in warnDict.Keys)
            {   // Iterate through each user snowflake logged in the dictionary.
                // Only make an embed if there are actually mentions.
                if (warnDict[snowflake].Count > 0)
                {
                    var mentionList = warnDict[snowflake];

                    var embed         = new DiscordEmbedBuilder();
                    var stringBuilder = new StringBuilder();

                    // Build header.
                    stringBuilder.Append($"__**{Generics.GetMention(snowflake)} has had {mentionList.Count + 1} total mention{(mentionList.Count == 1 ? String.Empty : @"s")} (including the most recent one)" +
                                         $"in {channel.Mention} in the last {Program.Settings.MaxActionAgeMonths} months:**__\n");

                    // Build link list.
                    stringBuilder.AppendJoin(' ',
                                             Generics.BuildLimitedLinkList(
                                                 links: mentionList
                                                 .Select(a => Generics.GetMessageUrl(a))
                                                 .ToArray(),
                                                 endMessage: @"... Too many to display...",
                                                 maxLength: 2000 - stringBuilder.Length));

                    embed.WithDescription(stringBuilder.ToString());
                    embed.WithTitle($"Previous mention{(mentionList.Count == 1 ? String.Empty : @"s")} found");
                    embed.WithColor(DiscordColor.Red);

                    await channel.SendMessageAsync(embed : embed);
                } // end if
            }     // end foreach
        }
コード例 #2
0
        /// <summary>Remove a reminder if it exists.</summary>
        public static async Task RemoveReminder(CommandContext ctx, Reminder reminder)
        {
            // It's a reminder, so let's remove it.

            // Let's build the command.
            using var command = new SqliteCommand(BotDatabase.Instance.DataSource)
                  {
                      CommandText = QQ_RemoveReminder
                  };

            SqliteParameter a = new SqliteParameter("$id", reminder.OriginalMessageId.ToString())
            {
                DbType = DbType.String
            };

            command.Parameters.Add(a);

            // Now that we have the old reminder, let's remove the old one from the database.
            await BotDatabase.Instance.ExecuteNonQuery(command);

            // Now let's respond.

            var discordEmbedBuilder = new DiscordEmbedBuilder(Generics.GenericEmbedTemplate(
                                                                  color: Generics.PositiveColor,
                                                                  description: Generics.PositiveDirectResponseTemplate(
                                                                      mention: ctx.Member.Mention,
                                                                      @"I able to remove the reminder you gave me!"),
                                                                  thumbnail: Generics.URL_REMINDER_DELETED,
                                                                  title: @"Removed reminder"));

            DateTimeOffset dto                   = DateTimeOffset.FromUnixTimeSeconds(reminder.Time * 60); // The reminder's DTO.
            TimeSpan       remainingTime         = dto.Subtract(DateTimeOffset.UtcNow);                    // The remaining time left for the reminder.
            string         originalAuthorMention = Generics.GetMention(reminder.User);

            discordEmbedBuilder.AddField(@"User", originalAuthorMention, true);
            discordEmbedBuilder.AddField(@"Time (UTC)", dto.ToString(Generics.DateFormat), true);
            discordEmbedBuilder.AddField(@"Notification Identifier", reminder.OriginalMessageId.ToString(), false);
            if (GetUsersToNotify(reminder.UsersToNotify, out string mentionsString))
            {
                discordEmbedBuilder.AddField(@"Users to mention", mentionsString, false);
            }
            discordEmbedBuilder.AddField(@"Remaining time", Generics.GetRemainingTime(dto), false);
            discordEmbedBuilder.AddField(@"Message", reminder.Text, false);

            // Send the response.
            await ctx.Channel.SendMessageAsync(embed : discordEmbedBuilder);
        }
コード例 #3
0
        public static async Task SeekWarns(CommandContext ctx, ulong[] memberIds)
        {
            const int MAX_FIELDS = 10;

            DiscordChannel actionLogChannel = await Program.BotClient.GetChannelAsync(Program.Settings.ActionChannelId);

            Dictionary <ulong, List <DiscordMessage> > warnDict =
                await QueryMemberMentions(memberIds.Distinct().ToList(), actionLogChannel, Program.Settings.MaxActionAgeMonths, ctx.Message);

            // Let's start paginating.
            var pages = new Page[warnDict.Keys.Count];
            int page  = 0;

            if (warnDict.Keys.Count > 0)
            {
                // Want to generate a page for each member.
                foreach (var member in warnDict.Keys)
                {
                    // We want a boolean to check first because if there's no key, we'll get an exception trying to get the count.
                    bool warnsFound = warnDict.ContainsKey(member) && warnDict[member].Count > 0;

                    var deb = new DiscordEmbedBuilder
                    {
                        Title       = $"Mentions found in action logs",
                        Description = Generics.NeutralDirectResponseTemplate(mention: ctx.Member.Mention,
                                                                             body: warnsFound ? // Warning, really f*****g long string ahead:
                                                                             $"I found {warnDict[member].Count} mention{(warnDict[member].Count == 1 ? String.Empty : @"s")} for " +
                                                                             $"{Generics.GetMention(member)} in {actionLogChannel.Mention} in the last {Program.Settings.MaxActionAgeMonths} months. " +
                                                                             $"{(warnDict[member].Count > MAX_FIELDS ? $"There are over {MAX_FIELDS}. I will only show the most recent." : String.Empty)}" :
                                                                             $"{ctx.Member.Mention}, I did not find any mentions for {Generics.GetMention(member)}. Good for them..."),
                        Color = warnsFound ? Generics.NegativeColor : Generics.NeutralColor
                    };

                    if (warnsFound)
                    {         // Only continue here if there are actually warns, otherwise just slap a footer on.
                        foreach (var message in warnDict[member])
                        {     // Generate a field for each detected message.
                            if (deb.Fields.Count < MAX_FIELDS)
                            { // Only continue if we have less than MAX_FIELDS fields.
                                // This SB is for all the content.
                                var stringBuilder = new StringBuilder();
                                // This SB is for all the misc information.
                                var stringBuilderFooter = new StringBuilder();

                                stringBuilder.Append($"{ChatObjects.Generics.GetMention(message.Author.Id)}: ");

                                stringBuilder.Append(message.Content);

                                if (message.Attachments.Count > 0)
                                {
                                    stringBuilderFooter.Append($"\n\n{Formatter.Bold(@"There is an image attached:")} ");

                                    stringBuilderFooter.Append(Formatter.MaskedUrl(@"Image", new Uri(message.Attachments[0].Url)));
                                } // end if

                                stringBuilderFooter.Append("\n\n");
                                stringBuilderFooter.Append(Formatter.MaskedUrl(@"Link", new Uri(Generics.GetMessageUrl(message))));

                                // We want to prefer the footer's information over the content. So let's figure out how much of the content we
                                // need to trim out.

                                var finalStringBuilder = new StringBuilder();

                                if (stringBuilder.Length + stringBuilderFooter.Length > 1000)
                                {     // We need to do some trimming.
                                    if (stringBuilder.Length > 0)
                                    { // Let's get the content in there.
                                        finalStringBuilder.Append(Generics.BuildLimitedString(
                                                                      originalString: stringBuilder.ToString(),
                                                                      endMessage: @". . . Unable to preview long message...",
                                                                      maxLength: 1000 - stringBuilderFooter.Length));
                                    }
                                    if (stringBuilderFooter.Length > 0)
                                    {   // Let's get the footer in there.
                                        finalStringBuilder.Append(stringBuilderFooter);
                                    }
                                }
                                else
                                {     // We don't need to do any trimming.
                                    if (stringBuilder.Length > 0)
                                    { // Let's get the content in there.
                                        finalStringBuilder.Append(stringBuilder);
                                    }
                                    if (stringBuilderFooter.Length > 0)
                                    {   // Let's get the footer in there.
                                        finalStringBuilder.Append(stringBuilderFooter);
                                    }
                                }

                                deb.AddField($"Action on {message.Timestamp.ToString(Generics.DateFormat)}", finalStringBuilder.ToString());
                            }
                            else
                            {          // Stop the loop if we have MAX_FIELDS fields.
                                break; // NON-SESE ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! !
                            } // end else
                        } // end foreach
                    } // end if

                    deb.WithFooter($"Page {page + 1}/{warnDict.Keys.Count}");

                    pages[page++] = new Page(embed: deb);
                } // end foreach
            }     // end if

            // Delete the message if it's the action channel so it's kind of out of the way and doesn't get logged again in the future.
            if (ctx.Message.ChannelId == Program.Settings.ActionChannelId)
            {
                await ctx.Message.DeleteAsync();
            }

            if (pages.Length > 1)
            {   // More than 1 page.
                var interactivity = Program.BotClient.GetInteractivity();

                await interactivity.SendPaginatedMessageAsync
                (
                    c : ctx.Channel,
                    u : ctx.User,
                    pages : pages,
                    emojis : Generics.DefaultPaginationEmojis
                );
            }
            else
            {   // Only one page, we want to send it as a regular embed instead.
                var anotherDeb = new DiscordEmbedBuilder(pages[0].Embed);

                // Clear the footer. We don't want the page count.
                anotherDeb.WithFooter(null, null);

                await ctx.Channel.SendMessageAsync(embed : anotherDeb);
            }
        }
コード例 #4
0
        /// <summary>Find any reminders that need to be triggered and trigger them.</summary>
        static async Task LookTriggerReminders(int timeNowMinutes)
        {
            using var command = new SqliteCommand(BotDatabase.Instance.DataSource)
                  {
                      CommandText = QQ_CheckRemindersElapsed
                  };

            SqliteParameter a = new SqliteParameter("$timenow", timeNowMinutes)
            {
                DbType = DbType.Int32
            };

            command.Parameters.Add(a);

            Reminder[] pendingReminders = (Reminder[])await BotDatabase.Instance.ExecuteReaderAsync(command,
                                                                                                    processAction : readReminders);

            // Check if there are any reminders
            if (pendingReminders.Length > 0)
            {   // There are reminders.
                using var delCommand = new SqliteCommand(BotDatabase.Instance.DataSource)
                      {
                          CommandText = QQ_DeleteRemindersElapsed
                      };

                delCommand.Parameters.Add(a);

                Task[] tasks = new Task[pendingReminders.Length + 1];
                tasks[0] = BotDatabase.Instance.ExecuteNonQuery(delCommand);

                for (int i = 0; i < pendingReminders.Length; i++)
                {
                    var reminder = pendingReminders[i];


                    DateTimeOffset reminderTime = DateTimeOffset.FromUnixTimeSeconds(reminder.Time * 60);
                    DateTimeOffset utcNow       = DateTimeOffset.UtcNow;

                    var      stringBuilder = new StringBuilder();
                    TimeSpan lateBy        = utcNow.Subtract(reminderTime);

                    DiscordEmbedBuilder deb = new DiscordEmbedBuilder()
                    {
                        Title       = "Notification",
                        Description = reminder.Text
                    };

                    deb.WithThumbnail(Generics.URL_REMINDER_EXCLAIM);
                    deb.AddField(@"Late by",
                                 value: String.Format("{0}day {1}hr {2}min {3}sec",
                                                      /*0*/ lateBy.Days,
                                                      /*1*/ lateBy.Hours,
                                                      /*2*/ lateBy.Minutes,
                                                      /*3*/ lateBy.Seconds));

                    // Get all the people we need to remind.
                    stringBuilder.Append(Generics.GetMention(reminder.User));

                    Array.ForEach(reminder.UsersToNotify,            // For every user (a), append them to sb in mention format <@id>.
                                  a => stringBuilder.Append($"{a} "));



                    tasks[i + 1] = (await Program.BotClient.GetChannelAsync(reminder.Channel))
                                   .SendMessageAsync(
                        content: stringBuilder.ToString(),
                        embed: deb.Build());
                }

                await Task.WhenAll(tasks);
            }
        }
コード例 #5
0
        public static async Task AddReminder(CommandContext ctx, string args)
        {   // Firstly get all the matches.
            MatchCollection regexMatches  = DateRegex.Matches(args);
            BitArray        regexCoverage = new BitArray(args.Length);
            var             dto           = ctx.Message.CreationTimestamp;
            List <ulong>    mentions      = new List <ulong>();

            // String processing - find the message and get the reminder end date.
            // To find what's not a date, we simply look for the first character that isn't in the boundaries of a Regex match.
            //
            // Structure of a possible string:
            //
            //             Date String | Message String
            //  DATE, DATE, DATE, DATE | message
            //
            // Beyond the Date String we want to stop processing time information as people may reference time in the message string, so we don't
            // erronously want that data added to the date string.


            // Populate the regexCoverage...
            foreach (Match match in regexMatches)
            {
                for (int i = match.Index; i < match.Index + match.Length; i++)
                {
                    regexCoverage[i] = true;
                }
            }

            // So I want to explain what I'm about to do here. Every value in regexCoverage[] indicates if that's part of the initial time
            // string. We want to use it to determine if something is a message or a time value, so if at any point, we run into something that
            // isn't a time string, we want to set every instance thereafter as false so we know it's part of a message.
            if (regexMatches.Count > 0)
            {
                bool value = regexCoverage[0];
                for (int k = 1; k < regexCoverage.Count; k++)
                {
                    if (!IsWhitespace(args[k]))
                    {
                        if (!regexCoverage[k] && value)
                        {
                            value = false;
                        }

                        if (!value)
                        {
                            regexCoverage[k] = value;
                        }
                    }
                }
            }
            // We need to figure out where the date string ends.
            string messageString = String.Empty;

            int  dateEndIndex = 0;
            bool messageFound = false;

            while (dateEndIndex < regexCoverage.Length && !messageFound)
            {
                char stringChar        = args[dateEndIndex];
                bool inRegexBoundaries = regexCoverage[dateEndIndex];

                // This checks to see if the character is non-white-space and outside of any RegEx boundaries.
                messageFound = !IsWhitespace(stringChar) && !inRegexBoundaries;

                // If not found, continue; otherwise, keep incrementing.
                if (!messageFound)
                {
                    dateEndIndex++;
                }
            }

            // If we aren't going out of bounds, let's set the string to this.
            if (dateEndIndex < regexCoverage.Length)
            {
                messageString = args.Substring(dateEndIndex);
            }

            // Get date information
            foreach (Match match in regexMatches)
            {
                // Only try to exclude Message String date information if a message string was found.
                if (!messageFound || (regexCoverage[match.Index] && regexCoverage[match.Index + match.Length - 1]))
                {
                    InterpretTime(match.Groups[1].Value, match.Groups[2].Value, ref dto);
                }
            }

            // Get mentions
            foreach (DiscordUser user in ctx.Message.MentionedUsers)
            {
                ulong id = user.Id;

                if (!user.IsBot && !mentions.Contains(id))
                {
                    mentions.Add(id);
                }
            }

            // At this point, now we have the DateTimeOffset describing when this reminder needs to be set off, and we have a message string if
            // any. So now we just need to make sure it's within reasonable boundaries, set the reminder, and notify the user.

            DateTimeOffset      maxtime = new DateTimeOffset(ctx.Message.CreationTimestamp.UtcDateTime).AddMonths(Program.Settings.MaxReminderTimeMonths);
            DiscordEmbedBuilder embed;

            bool sendErrorEmbed = false;

            if (dto.UtcTicks == ctx.Message.CreationTimestamp.UtcTicks)
            {   // No time was added.
                embed = Generics.GenericEmbedTemplate(
                    color: Generics.NegativeColor,
                    description: Generics.NegativeDirectResponseTemplate(
                        mention: ctx.Member.Mention,
                        body: @"I was unable able to add the reminder you gave me. You didn't supply me a valid time..."),
                    title: @"Unable to add reminder",
                    thumbnail: Generics.URL_REMINDER_GENERIC
                    );

                sendErrorEmbed = true;
            }
            else if (dto.UtcTicks > maxtime.UtcTicks)
            {   // More than our allowed time away.
                int maxMonths = Program.Settings.MaxReminderTimeMonths;

                embed = Generics.GenericEmbedTemplate(
                    color: Generics.NegativeColor,
                    description: Generics.NegativeDirectResponseTemplate(
                        mention: ctx.Member.Mention,
                        body: $"I was unable able to add the reminder you gave me. That's more than {maxMonths} month{(maxMonths > 0 ? @"s" : String.Empty)} away..."),
                    title: @"Unable to add reminder",
                    thumbnail: Generics.URL_REMINDER_GENERIC
                    );

                sendErrorEmbed = true;
            }
            else
            {   // Everything is good in the world... except that the world is burning, but that's not something we're worried about here, for
                // now...

                embed = Generics.GenericEmbedTemplate(
                    color: Generics.PositiveColor,
                    description: Generics.PositiveDirectResponseTemplate(
                        mention: ctx.Member.Mention,
                        body: @"I added the reminder you gave me!"),
                    title: @"Add reminder",
                    thumbnail: Generics.URL_REMINDER_GENERIC
                    );

                Reminder reminder = new Reminder(
                    originalMessageId: ctx.Message.Id.ToString(),
                    text: messageString.Length.Equals(0) ? @"n/a" : messageString.ToString(),
                    time: (int)(dto.ToUnixTimeSeconds() / 60),
                    user: ctx.Member.Id,
                    channel: ctx.Channel.Id,
                    usersToNotify: mentions.Select(a => Generics.GetMention(a)).ToArray());

                embed.AddField(@"User", ctx.Member.Mention, true);
                embed.AddField(@"Time (UTC)", dto.ToString(Generics.DateFormat), true);
                embed.AddField(@"Remaining time", Generics.GetRemainingTime(dto), true);
                embed.AddField(@"Notification Identifier", reminder.OriginalMessageId.ToString(), false);

                if (GetUsersToNotify(reminder.UsersToNotify, out string mentionsString))
                {
                    embed.AddField(@"Users to mention", mentionsString, false);
                }

                // Let's build the command.
                using var command = new SqliteCommand(BotDatabase.Instance.DataSource)
                      {
                          CommandText = QQ_AddReminder
                      };

                SqliteParameter a = new SqliteParameter("$id", reminder.OriginalMessageId.ToString())
                {
                    DbType = DbType.String
                };

                SqliteParameter b = new SqliteParameter("$userid", reminder.User)
                {
                    DbType = DbType.String
                };

                SqliteParameter c = new SqliteParameter("$channelid", reminder.Channel)
                {
                    DbType = DbType.String
                };

                SqliteParameter d = new SqliteParameter("$message", reminder.Text)
                {
                    DbType = DbType.String
                };

                SqliteParameter e = new SqliteParameter("$time", reminder.Time)
                {
                    DbType = DbType.Int32
                };

                var stringBuilder = new StringBuilder();
                stringBuilder.AppendJoin(' ', reminder.UsersToNotify);

                SqliteParameter f = new SqliteParameter("$mention", stringBuilder.ToString())
                {
                    DbType = DbType.String
                };

                command.Parameters.AddRange(new SqliteParameter[] { a, b, c, d, e, f });

                await BotDatabase.Instance.ExecuteNonQuery(command);

                // Send the response.
                await ctx.Channel.SendMessageAsync(embed : embed);
            }

            if (sendErrorEmbed)
            {
                var a = ctx.Channel.SendMessageAsync(embed: embed);
                var b = GenericResponses.HandleInvalidArguments(ctx);

                await Task.WhenAll(a, b);
            }
        }
コード例 #6
0
        /// <summary>List all the reminders</summary>
        public static async Task ListReminders(CommandContext ctx)
        {
            Reminder[] reminders = await ReadTable();

            // Check if there are any notifications. If there are none, let the user know.
            if (reminders.Length > 0)
            {   // There are reminders.
                var         interactivity = Program.BotClient.GetInteractivity();
                List <Page> pages         = new List <Page>();

                var deb = new DiscordEmbedBuilder();

                int count   = 0;
                int curPage = 1;

                // Paginate all the results.
                const int REMINDERS_PER_PAGE = 5;
                for (int i = 0; i < reminders.Length; i++)
                {
                    Reminder reminder = reminders[i];



                    var dto = DateTimeOffset.FromUnixTimeSeconds(reminder.Time * 60);

                    var valueStringBuilder = new StringBuilder();

                    valueStringBuilder.Append($"{Generics.GetMention(reminder.User)}: {reminder.Text}\n");
                    if (GetUsersToNotify(reminder.UsersToNotify, out string mentionsString))
                    {
                        valueStringBuilder.Append($"**Users to mention:** {mentionsString}\n");
                    }
                    valueStringBuilder.Append($"**Id:** {reminder.OriginalMessageId}\n");
                    valueStringBuilder.Append($"**Remaining time:** {Generics.GetRemainingTime(dto)}");

                    #region a bunny

                    //                      .".
                    //                     /  |
                    //                    /  /
                    //                   / ,"
                    //       .-------.--- /
                    //      "._ __.-/ o. o\
                    //         "   (    Y  )
                    //              )     /
                    //             /     (
                    //            /       Y
                    //        .-"         |
                    //       /  _     \    \
                    //      /    `. ". ) /' )
                    //     Y       )( / /(,/
                    //    ,|      /     )
                    //   ( |     /     /
                    //    " \_  (__   (__        [nabis]
                    //        "-._,)--._,)
                    //  o < bunny poopy l0l
                    // ------------------------------------------------
                    // This ASCII pic can be found at
                    // https://asciiart.website/index.php?art=animals/rabbits

                    #endregion a bunny

                    string name = dto.ToString(Generics.DateFormat);

                    deb.AddField(name, valueStringBuilder.ToString());
                    count++;

                    if (count == REMINDERS_PER_PAGE || i == reminders.Length - 1)
                    {   // Create a new page.
                        deb.WithDescription(Generics.NeutralDirectResponseTemplate(
                                                mention: ctx.User.Mention,
                                                body: $"Hello {ctx.Member.Mention}, please note you are the only one who can react to this message.\n\n" +
                                                $"**Showing {count} reminders out of a total of {reminders.Length}.**"));
                        deb.WithTitle($"Reminders Page {curPage}/{Math.Ceiling((float)reminders.Length / (float)REMINDERS_PER_PAGE)}");
                        deb.WithColor(Generics.NeutralColor);
                        deb.WithThumbnail(Generics.URL_REMINDER_GENERIC);

                        pages.Add(new Page(embed: deb));
                        count = 0;
                        curPage++;

                        deb = new DiscordEmbedBuilder();
                    } // end if
                }     // end for

                await interactivity.SendPaginatedMessageAsync(ctx.Channel, ctx.User, pages, emojis : Generics.DefaultPaginationEmojis);
            }
            else
            {   // There are no reminders.
                await ctx.Channel.SendMessageAsync(
                    embed : Generics.GenericEmbedTemplate(
                        color: Generics.NeutralColor,
                        description: Generics.NeutralDirectResponseTemplate(
                            mention: ctx.Member.Mention,
                            body: "there are no reminders."),
                        thumbnail: Generics.URL_SPEECH_BUBBLE,
                        title: "Reminders"));
            }
        }