Beispiel #1
0
        public static ICommandResult EndPoll(DiscordUserMessageContext context)
        {
            Poll p = Poll.GetPoll(context.Channel);

            if (p != null && p.Active)
            {
                return(Poll.End(context.Channel));
            }
            return(new ErrorResult("No poll currently in progress"));
        }
        public static async Task <ICommandResult> ChangeUsername(DiscordUserMessageContext context, [JoinRemainingParameters, HelpText("The new username for the bot")] string username)
        {
            if (username.Length > 32)
            {
                return(new ErrorResult($"Username must be no more than 32 characters in length. ({username.Length} > 32)"));
            }

            await context.Bot.Client.CurrentUser.ModifyAsync(bot => bot.Username = username);

            return(new SuccessResult("Changed username successfully"));
        }
Beispiel #3
0
        public static ICommandResult Vote(DiscordUserMessageContext context, [DisplayName("option number|option text"), HelpText("The poll option text or number to vote for"), JoinRemainingParameters] string option)
        {
            Poll p = Poll.GetPoll(context.Channel);

            if (p != null && !p.Anonymous && p.Active)
            {
                bool update = false;
                if (context.MessageAuthor.IsBot)
                {
                    return(new ErrorResult("BOTs can't vote in polls"));
                }

                if (p.Voters.ContainsKey(context.MessageAuthor))
                {
                    update = true;
                    if (p.MinutesLeft < 0.5)
                    {
                        return(new ErrorResult("Too late to change your vote! Sorry."));
                    }
                }

                bool num = true;
                if (int.TryParse(option, out int i) && i > 0 && i <= p.Options.Count)
                {
                    p.Voters[context.MessageAuthor] = p.Options[i - 1];
                }
                else if (p.Options.Any(x => x.Text == option))
                {
                    num = false;
                    if (p.Options.Count(x => x.Text == option) == 1)
                    {
                        p.Voters[context.MessageAuthor] = p.Options.First(x => x.Text == option);
                    }
                    else
                    {
                        return(new ErrorResult("There are multiple options with the same text. Please vote by number instead."));
                    }
                }
                else
                {
                    return(new ErrorResult("That poll option doesn't exist"));
                }

                return(new SuccessResult(!update ? $"<@{context.MessageAuthor.Id}>: Vote for {(num ? "option #" : "'")}{option}{(num ? "" : "'")} acknowledged" : $"<@{context.MessageAuthor.Id}>: Vote update to {(num ? "option #" : "'")}{option}{(num ? "" : "'")} acknowledged"));
            }
            else
            {
                if (p != null && p.Anonymous)
                {
                    return(new ErrorResult($"The current poll is anonymous. Please use `!anonvote {p.Id} <number|option>` in a direct message to me to vote."));
                }
                return(new ErrorResult("No poll currently in progress"));
            }
        }
Beispiel #4
0
        public static async Task <ICommandResult> Description(DiscordUserMessageContext context, ChannelDescriptionAction action, [HelpText("The dynamic description text, surround commands in {{ }}"), JoinRemainingParameters] string text = "")
        {
            var channelDescriptions = context.Bot.ChannelDescriptions.Descriptions ?? new Dictionary <ulong, string>();

            var textChannel = (ITextChannel)context.Channel;

            switch (action)
            {
            case ChannelDescriptionAction.Get:
                if (channelDescriptions.ContainsKey(textChannel.Id))
                {
                    return(new SuccessResult($"```{channelDescriptions[textChannel.Id].Replace("```", "`​`​`​")}```"));
                }
                else
                {
                    return(new ErrorResult("This channel does not have a dynamic description"));
                }

            case ChannelDescriptionAction.Set:
                await textChannel.ModifyAsync(x => x.Topic = "Loading dynamic description...");

                if (channelDescriptions.ContainsKey(textChannel.Id))
                {
                    channelDescriptions[textChannel.Id] = text;
                }
                else
                {
                    channelDescriptions.Add(textChannel.Id, text);
                }

                context.Bot.ChannelDescriptions.Descriptions = channelDescriptions;
                context.Bot.ChannelDescriptions.SaveConfig();
                return(new SuccessResult("Description updated sucessfully"));

            case ChannelDescriptionAction.Remove:
                if (channelDescriptions.ContainsKey(textChannel.Id))
                {
                    await textChannel.ModifyAsync(x => x.Topic = "");

                    channelDescriptions.Remove(textChannel.Id);
                    context.Bot.ChannelDescriptions.Descriptions = channelDescriptions;
                    context.Bot.ChannelDescriptions.SaveConfig();
                    return(new SuccessResult("Description removed sucessfully"));
                }
                else
                {
                    return(new ErrorResult("This channel does not have a dynamic description"));
                }

            default:
                return(new ErrorResult("That option doesn't exist (not really sure how you got here)"));
            }
        }
Beispiel #5
0
        public static async Task <ICommandResult> StartAnonPoll(DiscordUserMessageContext context, [HelpText("The amount of time the poll should be active (in minutes)")] double length, string option1, string option2, [JoinRemainingParameters, HelpText("More space-separated poll options")] string[] otherOptions = null)
        {
            var options = new List <string> {
                option1, option2
            };

            if (otherOptions != null)
            {
                options.AddRange(otherOptions);
            }
            return(await Poll.CreatePoll(context, length, options, true));
        }
Beispiel #6
0
        public static async Task <ICommandResult> Analytics(DiscordUserMessageContext context, bool includeMessageText = false)
        {
            await(await context.Channel.SendMessageAsync("This is going to take a while")).AddReactionAsync(CommandTools.LoadingEmote);

            using (context.Channel.EnterTypingState())
            {
                // Channel Name => User Name => Date => Hour
                List <string> data = new List <string>();
                data.Add($"MessageID\tChannel\tUser\tIsBot\tTimestamp\tUnixTimestamp\tEditedTimestamp\tUnixEditedTimestamp\tMessageLength\tEmbedType\tHasAttachment\tReactionCount{(includeMessageText ? "\tMessage Text" : "")}");
                var channels = await context.Guild.GetTextChannelsAsync();

                foreach (ITextChannel channel in channels)
                {
                    ChannelPermissions permissions = (await context.Guild.GetCurrentUserAsync()).GetPermissions(channel);
                    if (!permissions.ViewChannel || !permissions.ReadMessageHistory)
                    {
                        continue;
                    }

                    var pages = channel.GetMessagesAsync(int.MaxValue);
                    pages.ForEach(
                        page =>
                    {
                        foreach (IMessage message in page)
                        {
                            var timestampLocal = TimeZoneInfo.ConvertTimeBySystemTimeZoneId(message.Timestamp, context.Bot.DefaultTimeZone);
                            DateTimeOffset?editedTimestampLocal = null;
                            if (message.EditedTimestamp != null)
                            {
                                editedTimestampLocal = TimeZoneInfo.ConvertTimeBySystemTimeZoneId(message.EditedTimestamp.Value, context.Bot.DefaultTimeZone);
                            }
                            data.Add($"{message.Id}\t{message.Channel.Name}\t{message.Author}\t{message.Author.IsBot}\t{timestampLocal.DateTime:G}\t{timestampLocal.ToUnixTimeSeconds()}\t{editedTimestampLocal?.ToString("G") ?? ""}\t{editedTimestampLocal?.ToUnixTimeSeconds().ToString() ?? ""}\t{new System.Globalization.StringInfo(message.Content).LengthInTextElements}\t{message.Embeds.FirstOrDefault()?.Type.ToString() ?? ""}\t{message.Attachments.Count > 0}\t{(message as IUserMessage)?.Reactions.Count ?? 0}{(includeMessageText ? $"\t{message.Content.Replace("\n", "␊").Replace("\r", "")}" : "")}");
                        }
                    });
                }

                File.WriteAllLines(Config.BasePath + $"analytics-{context.Guild.Id}.txt", data);

                if (!includeMessageText)
                {
                    using (var stream = File.OpenRead(Config.BasePath + $"analytics-{context.Guild.Id}.txt"))
                    {
                        await context.Channel.SendFileAsync(stream, $"analytics-{context.Guild.Id}-{DateTimeOffset.Now.ToUnixTimeSeconds()}.txt");
                    }
                }
                else
                {
                    await context.Reply($"Finished creating analytics file. Saved as `analytics-{context.Guild.Id}.txt` ({Math.Round(new FileInfo(Config.BasePath + $"analytics-{context.Guild.Id}.txt").Length / 1048576d, 2)} MB)");
                }
            }
            return(new SuccessResult());
        }
        public static async Task <ICommandResult> File(DiscordUserMessageContext context, FileAction action, string filename = null)
        {
            switch (action)
            {
            case FileAction.Get:
                if (filename == null)
                {
                    return(new ErrorResult("Please provide a filename"));
                }
                if (!context.Bot.FileNames.Contains(filename))
                {
                    return(new ErrorResult("The provided filename was not a valid option"));
                }
                await context.Channel.SendFileAsync(Config.BasePath + filename);

                return(new SuccessResult());

            case FileAction.Put:
                if (filename == null)
                {
                    return(new ErrorResult("Please provide a filename"));
                }
                if (!context.Bot.FileNames.Contains(filename))
                {
                    return(new ErrorResult("The provided filename was not a valid option"));
                }
                if (context.Message.Attachments.Count == 0)
                {
                    return(new ErrorResult("Please attach a file with your message"));
                }
                var attachment = context.Message.Attachments.First();
                using (var fileStream = System.IO.File.Create(Config.BasePath + filename))
                {
                    await(await new HttpClient().GetStreamAsync(attachment.Url)).CopyToAsync(fileStream);
                }
                return(new SuccessResult("File successfully replaced"));

            case FileAction.List:
                return(new SuccessResult($"```\nAvailable Files:\n\n{string.Join("\n", context.Bot.FileNames)}\n```"));

            default:
                return(new ErrorResult("Unknown option"));
            }
        }
Beispiel #8
0
        public static ICommandResult SetPrefix(DiscordUserMessageContext context, [JoinRemainingParameters, HelpText("The new prefix for this channel or server (up to 16 characters)")] string prefix)
        {
            if (prefix.Length > 16)
            {
                return(new ErrorResult("Prefix must be no more than 16 characters in length"));
            }

            bool  server = false;
            ulong id;

            if (context.ChannelType == ChannelType.Text)
            {
                id     = context.Guild?.Id ?? context.Channel.Id;
                server = context.Guild != null;
            }
            else
            {
                id = context.Channel.Id;
            }

            if (context.Bot.Settings.CustomPrefixes != null)
            {
                if (context.Bot.Settings.CustomPrefixes.ContainsKey(id))
                {
                    context.Bot.Settings.CustomPrefixes[id] = prefix;
                }
                else
                {
                    context.Bot.Settings.CustomPrefixes.Add(id, prefix);
                }
            }
            else
            {
                context.Bot.Settings.CustomPrefixes = new Dictionary <ulong, string>
                {
                    [id] = prefix
                };
            }

            context.Bot.Settings.SaveConfig();

            return(new SuccessResult($"Prefix set to `{prefix}` for this {(server ? "server" : "channel")}"));
        }
        public static async Task <ICommandResult> Delete(DiscordUserMessageContext context, [HelpText("The number of message to delete (up to 99)"), JoinRemainingParameters] byte number)
        {
            if (number >= 100)
            {
                return(new ErrorResult("The maximum number of messages to delete is 99 (plus the command message)"));
            }

            var toDelete = (await context.Channel.GetMessagesAsync(number + 1).FlattenAsync()).ToArray();

            var bulkDelete = toDelete.Where(msg => msg.Timestamp + TimeSpan.FromDays(14) > DateTimeOffset.Now);
            var remaining  = toDelete.Except(bulkDelete);

            await((ITextChannel)context.Channel).DeleteMessagesAsync(bulkDelete);
            foreach (IMessage msg in remaining)
            {
                await msg.DeleteAsync();
            }

            return(new SuccessResult());
        }
Beispiel #10
0
        public static async Task <ICommandResult> Grammar(DiscordUserMessageContext context, GrammarAction command)
        {
            switch (command)
            {
            case GrammarAction.Enable:
                await context.Bot.Grammar.Start();

                break;

            case GrammarAction.Disable:
                await context.Bot.Grammar.Stop();

                break;

            default:
                return(new ErrorResult("Not an option"));
            }

            return(new SuccessResult());
        }
Beispiel #11
0
        public static ICommandResult Reminders(DiscordUserMessageContext context, ReminderAction action = ReminderAction.List, int id = 0)
        {
            var reminders = context.Bot.Reminders.Reminders.Where(x => x.ReceiverId == context.Message.Author.Id).OrderBy(x => x.Timestamp);

            switch (action)
            {
            case ReminderAction.List:
                StringBuilder builder = new StringBuilder();
                builder.AppendLine("```");
                builder.AppendLine($"{"ID",-4}{"Date",-25}{"Sender",-20}Message");
                if (!reminders.Any())
                {
                    return(new SuccessResult("No reminders!"));
                }
                int counter = 1;

                foreach (var reminder in reminders)
                {
                    builder.AppendLine($"{counter++,-4}{(reminder.Timestamp - DateTimeOffset.Now).ToShortString(),-25}{context.Bot.Client.GetUser(reminder.SenderId).Username,-20}{reminder.Message}");
                }
                builder.AppendLine("```");
                return(new SuccessResult(builder.ToString()));

            case ReminderAction.Delete:
                if (id == 0)
                {
                    return(new ErrorResult("Please enter a reminder ID"));
                }
                if (id < 1 || !reminders.Any())
                {
                    return(new ErrorResult("That reminder does not exist"));
                }
                var r = reminders.ToList()[id - 1];
                context.Bot.Reminders.Reminders.Remove(r);
                context.Bot.Reminders.SaveConfig();
                return(new SuccessResult($"Reminder '{r.Message}' deleted"));

            default:
                return(new ErrorResult(new NotImplementedException("This feature doesn't exist")));
            }
        }
Beispiel #12
0
        private async Task Client_MessageReceived(SocketMessage arg)
        {
            var    context       = new DiscordUserMessageContext((IUserMessage)arg, this);
            string commandPrefix = CommandTools.GetCommandPrefix(context, context.Channel);
            var    status        = Statuses.Statuses.GetValueOrDefault(arg.Author.Id) ??
                                   new UserStatusInfo
            {
                StatusLastChanged = DateTimeOffset.MinValue,
                LastOnline        = DateTimeOffset.MinValue,
                Game            = null,
                StartedPlaying  = null,
                LastMessageSent = DateTimeOffset.MinValue
            };

            status.LastMessageSent           = DateTimeOffset.Now;
            Statuses.Statuses[arg.Author.Id] = status;
            if (arg.Content.Trim().StartsWith(commandPrefix) && !arg.Author.IsBot)
            {
                await CommandRunner.Run(arg.Content, context, commandPrefix, false);
            }
        }
Beispiel #13
0
        public static async Task <ICommandResult> ChangeAvatar(DiscordUserMessageContext context, [JoinRemainingParameters, HelpText("The image URL for the bot's avatar")] string url)
        {
            try
            {
                await context.Message.AddReactionAsync(CommandTools.LoadingEmote);

                using (var fileStream = System.IO.File.Create("avatar.png"))
                {
                    var stream = await new HttpClient().GetStreamAsync(url);
                    stream.CopyTo(fileStream);
                }
                await context.Bot.Client.CurrentUser.ModifyAsync(bot => bot.Avatar = new Image("avatar.png"));

                await context.Message.Channel.SendFileAsync("avatar.png", "Updated avatar successfully");

                await context.Message.RemoveReactionAsync(CommandTools.LoadingEmote, context.Bot.Client.CurrentUser);

                return(new SuccessResult());
            }
            catch (Exception ex)
            {
                return(new ErrorResult(ex));
            }
        }
Beispiel #14
0
        public static async Task <ICommandResult> Remind(DiscordUserMessageContext context, [DisplayName("username or @mention"), HelpText("The user to remind")] string user, [HelpText("The number of hours from now to send the reminder")] double hours, [JoinRemainingParameters, HelpText("The message to send as a reminder")] string message)
        {
            DateTimeOffset targetTime = DateTimeOffset.UtcNow + TimeSpan.FromHours(hours);

            return(await CreateReminder(context, user, message, targetTime));
        }
Beispiel #15
0
        public static async Task <ICommandResult> Remind(DiscordUserMessageContext context, [DisplayName("username or @mention"), HelpText("The user to remind")] string user, [DisplayName("reminder time"), HelpText("The the time at which to send the reminder")] string timestamp, [JoinRemainingParameters, HelpText("The message to send as a reminder")] string message)
        {
            DateTimeOffset targetTime;
            Match          regexMatch    = null;
            DayOfWeek      dayOfReminder = 0;

            timestamp = timestamp.Trim().ToLower();

            // tacked on to support today/tonight/tomorrow in same regex
            bool reminderIsToday   = false;
            bool reminderIsTonight = false;

            if ((regexMatch = DeltaTimeRegex.Match(timestamp)).Success)
            {
                // delta time from regex
                int days    = 0;
                int hours   = 0;
                int minutes = 0;
                int seconds = 0;
                if (regexMatch.Groups["days"].Success)
                {
                    days = int.Parse(regexMatch.Groups["days"].Value);
                }
                if (regexMatch.Groups["hours"].Success)
                {
                    hours = int.Parse(regexMatch.Groups["hours"].Value);
                }
                if (regexMatch.Groups["minutes"].Success)
                {
                    minutes = int.Parse(regexMatch.Groups["minutes"].Value);
                }
                if (regexMatch.Groups["seconds"].Success)
                {
                    seconds = int.Parse(regexMatch.Groups["seconds"].Value);
                }

                targetTime = DateTimeOffset.UtcNow + new TimeSpan(days, hours, minutes, seconds);

                // so we don't trip up the later use of regexMatch for absolute times
                regexMatch = null;
            }
            else if ((regexMatch = TomorrowRegex.Match(timestamp)).Success)
            {
                switch (regexMatch.Groups["dayspec"].Value.ToLowerInvariant())
                {
                case "morrow":
                    // tomorrow, local time
                    dayOfReminder = TimeZoneInfo.ConvertTimeBySystemTimeZoneId(DateTimeOffset.Now, context.Bot.DefaultTimeZone).AddDays(1).DayOfWeek;
                    break;

                case "night":
                    reminderIsTonight = true;
                    reminderIsToday   = true;
                    dayOfReminder     = TimeZoneInfo.ConvertTimeBySystemTimeZoneId(DateTimeOffset.Now, context.Bot.DefaultTimeZone).DayOfWeek;
                    break;
                }
            }
            else if ((regexMatch = DayOfWeekRegex.Match(timestamp)).Success)
            {
                // absolute time from day of week, local time
                dayOfReminder = Enum.Parse <DayOfWeek>(regexMatch.Groups["dow"].Value, true);
            }
            else
            {
                return(new ErrorResult("Invalid reminder time. Valid options include: 'tomorrow', a day of the week, a full date and time string, or a number of hours in the future."));
            }

            if (regexMatch != null)
            {
                DateTimeOffset todayLocal = TimeZoneInfo.ConvertTimeBySystemTimeZoneId(DateTimeOffset.Now, context.Bot.DefaultTimeZone);
                targetTime = todayLocal;

                if (!reminderIsToday)
                {
                    // a minimum of 1 day, so "remind me tuesday" on a tuesday will go for next week
                    do
                    {
                        targetTime = targetTime.AddDays(1);
                    } while (targetTime.DayOfWeek != dayOfReminder);
                }

                // defaults to 8AM
                // the "tonight" default for 8PM will be accounted for later
                int targetHour   = 8;
                int targetMinute = 0;
                if (regexMatch.Groups["time"].Success)
                {
                    string[] timeComponents = regexMatch.Groups["time"].Value.Split(new char[] { ':' }, 2);
                    targetHour = int.Parse(timeComponents[0]);
                    if (timeComponents.Length >= 2)
                    {
                        targetMinute = int.Parse(timeComponents[1]);
                    }
                }

                if ((!regexMatch.Groups["ampm"].Success && reminderIsTonight) || (regexMatch.Groups["ampm"].Success && regexMatch.Groups["ampm"].Value.ToLower().Trim() == "pm"))
                {
                    // afternoon time, add 12 to hour
                    // either "PM" or "tonight" (without an explicit specification) = use afternoon times
                    targetHour += 12;
                }

                targetTime = new DateTimeOffset(targetTime.Year, targetTime.Month, targetTime.Day, targetHour, targetMinute, 0, targetTime.Offset);
            }

            return(await CreateReminder(context, user, message, targetTime));
        }
Beispiel #16
0
        public static async Task <ICommandResult> AnonVote(DiscordUserMessageContext context, [DisplayName("poll ID"), HelpText("The ID number of the anonymous poll")] int pollId, [DisplayName("option number|option text"), HelpText("The poll option text or number to vote for"), JoinRemainingParameters] string option)
        {
            Poll p = Poll.GetPollById(pollId);

            if (p != null && p.Active)
            {
                if (await p.Channel.GetUserAsync(context.MessageAuthor.Id) == null)
                {
                    return(new ErrorResult("That poll doesn't exist"));
                }

                bool update = false;
                if (context.MessageAuthor.IsBot)
                {
                    return(new ErrorResult($"BOTs can't vote in polls"));
                }


                if (p.Voters.ContainsKey(context.MessageAuthor))
                {
                    update = true;
                    if (p.MinutesLeft < 0.5)
                    {
                        return(new ErrorResult("Too late to change your vote! Sorry."));
                    }
                }

                try
                {
                    bool num = true;
                    if (int.TryParse(option, out int i) && i > 0 && i <= p.Options.Count)
                    {
                        p.Voters[context.MessageAuthor] = p.Options[i - 1];
                    }
                    else if (p.Options.Any(x => x.Text == option))
                    {
                        num = false;
                        if (p.Options.Count(x => x.Text == option) == 1)
                        {
                            p.Voters[context.MessageAuthor] = p.Options.First(x => x.Text == option);
                        }
                        else
                        {
                            return(new ErrorResult("There are multiple options with the same text. Please vote by number instead."));
                        }
                    }
                    else
                    {
                        return(new ErrorResult("That poll option doesn't exist"));
                    }

                    return(new SuccessResult(!update ? $"<@{context.MessageAuthor.Id}>: Vote for {(num ? "option #" : "'")}{option}{(num ? "" : "'")} acknowledged" : $"<@{context.MessageAuthor.Id}>: Vote update to {(num ? "option #" : "")}{option}{(num ? "" : "'")} acknowledged"));
                }
                catch (Exception ex)
                {
                    return(new ErrorResult(ex));
                }
            }
            else
            {
                return(new ErrorResult("No poll with that id currently in progress"));
            }
        }
Beispiel #17
0
 public static async Task <ICommandResult> Remind(DiscordUserMessageContext context, [DisplayName("username or @mention"), HelpText("The user to remind")] string user, [HelpText("The date and/or time to send the reminder")] DateTimeOffset date, [JoinRemainingParameters, HelpText("The message to send as a reminder")] string message)
 {
     return(await CreateReminder(context, user, message, date));
 }