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")); }
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")); } }
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)")); } }
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)); }
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")); } }
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()); }
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()); }
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"))); } }
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); } }
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)); } }
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)); }
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)); }
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")); } }
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)); }