public async Task DeleteAsync(CommandContext ctx, [Description("Reminder ID.")] params int[] ids) { if (!ids.Any()) { throw new InvalidCommandUsageException("Missing IDs of reminders to remove."); } if (!this.Shared.RemindExecuters.ContainsKey(ctx.User.Id)) { throw new CommandFailedException("You have no reminders scheduled."); } var eb = new StringBuilder(); foreach (int id in ids) { if (!this.Shared.RemindExecuters[ctx.User.Id].Any(texec => texec.Id == id)) { eb.AppendLine($"Reminder with ID {Formatter.Bold(id.ToString())} does not exist (or is not scheduled by you)!"); continue; } await SavedTaskExecutor.UnscheduleAsync(this.Shared, ctx.User.Id, id); } if (eb.Length > 0) { await this.InformFailureAsync(ctx, $"Action finished with following warnings/errors:\n\n{eb.ToString()}"); } else { await this.InformAsync(ctx, "Successfully removed all specified reminders.", important : false); } }
public async Task DeleteAsync(CommandContext ctx, [Description("Channel for which to remove reminders.")] DiscordChannel channel = null) { if (!(channel is null) && channel.Type != ChannelType.Text) { throw new InvalidCommandUsageException("You must specify a text channel."); } if (!await ctx.WaitForBoolReplyAsync("Are you sure you want to remove all your reminders" + (channel is null ? "?" : $"in {channel.Mention}?"))) { return; } List <DatabaseReminder> reminders; using (DatabaseContext db = this.Database.CreateContext()) { if (channel is null) { reminders = await db.Reminders.Where(r => r.UserId == ctx.User.Id).ToListAsync(); } else { reminders = await db.Reminders.Where(r => r.UserId == ctx.User.Id && r.ChannelId == channel.Id).ToListAsync(); } } await Task.WhenAll(reminders.Select(r => SavedTaskExecutor.UnscheduleAsync(this.Shared, ctx.User.Id, r.Id))); await this.InformAsync(ctx, "Successfully removed the specified reminders.", important : false); }
public async Task DeleteAsync(CommandContext ctx, [Description("Reminder ID.")] params int[] ids) { if (ids is null || !ids.Any()) { throw new InvalidCommandUsageException("Missing IDs of the reminders to remove"); } if (!this.Shared.RemindExecuters.TryGetValue(ctx.User.Id, out ConcurrentDictionary <int, SavedTaskExecutor> texecs)) { throw new CommandFailedException("You currently have no reminders schedule."); } var sb = new StringBuilder(); foreach (int id in ids) { if (!texecs.TryGetValue(id, out _)) { sb.AppendLine($"Reminder with ID {Formatter.Bold(id.ToString())} does not exist (or it is not scheuled by you)!"); continue; } await SavedTaskExecutor.UnscheduleAsync(this.Shared, ctx.User.Id, id); } if (sb.Length > 0) { await this.InformOfFailureAsync(ctx, $"Action finished with the following warnings/errors:\n\n{sb.ToString()}"); } else { await this.InformAsync(ctx, "Successfully removed all of the specified remidners.", important : false); } }
private async Task AddReminderAsync(CommandContext ctx, TimeSpan timespan, DiscordChannel channel, string message, bool repeat = false) { if (string.IsNullOrWhiteSpace(message)) { throw new InvalidCommandUsageException("Missing time or repeat string."); } if (message.Length > 250) { throw new InvalidCommandUsageException("Message must be shorter than 250 characters."); } if (!(channel is null) && !channel.PermissionsFor(ctx.Member).HasPermission(Permissions.AccessChannels | Permissions.SendMessages)) { throw new CommandFailedException("You cannot send reminder to that channel!"); } if (channel is null && await ctx.Client.CreateDmChannelAsync(ctx.User.Id) is null) { throw new CommandFailedException("I cannot send DMs to you, please enable it so that I can remind you."); } bool privileged; using (DatabaseContext db = this.Database.CreateContext()) privileged = db.PrivilegedUsers.Any(u => u.UserId == ctx.User.Id); if (!ctx.Client.CurrentApplication.Owners.Any(o => o.Id == ctx.User.Id) && !privileged) { if (timespan < TimeSpan.Zero || timespan.TotalMinutes < 1 || timespan.TotalDays > 31) { throw new InvalidCommandUsageException("Time span cannot be less than 1 minute or greater than 31 days."); } if (this.Shared.RemindExecuters.TryGetValue(ctx.User.Id, out ConcurrentDictionary <int, SavedTaskExecutor> texecs) && texecs.Count >= 20) { throw new CommandFailedException("You cannot have more than 20 reminders scheduled!"); } } DateTimeOffset when = DateTimeOffset.Now + timespan; var task = new SendMessageTaskInfo(channel?.Id ?? 0, ctx.User.Id, message, when, repeat, timespan); await SavedTaskExecutor.ScheduleAsync(this.Shared, this.Database, ctx.Client, task); if (repeat) { await this.InformAsync(ctx, StaticDiscordEmoji.AlarmClock, $"I will repeatedly remind {channel?.Mention ?? "you"} every {Formatter.Bold(timespan.Humanize(4, minUnit: TimeUnit.Second))} to:\n\n{message}", important : false); } else { await this.InformAsync(ctx, StaticDiscordEmoji.AlarmClock, $"I will remind {channel?.Mention ?? "you"} in {Formatter.Bold(timespan.Humanize(4, minUnit: TimeUnit.Second))} ({when.ToUtcTimestamp()}) to:\n\n{message}", important : false); } }
public async Task TempBanAsync(CommandContext ctx, [Description("User (doesn't have to be a member).")] DiscordUser user, [Description("Time span.")] TimeSpan timespan, [RemainingText, Description("Reason.")] string reason = null) { if (user.Id == ctx.User.Id) { throw new CommandFailedException("You can't ban yourself."); } await ctx.Guild.BanMemberAsync(user.Id, 0, ctx.BuildInvocationDetailsString(reason)); DateTime until = DateTime.UtcNow + timespan; await this.InformAsync(ctx, $"{Formatter.Bold(ctx.User.Username)} BANNED {Formatter.Bold(user.ToString())} until {Formatter.Bold(until.ToLongTimeString())} UTC!"); var task = new UnbanTaskInfo(ctx.Guild.Id, user.Id, until); await SavedTaskExecutor.ScheduleAsync(this.Shared, this.Database, (DiscordClientImpl)ctx.Client, task); }
public async Task TempBanAsync(CommandContext ctx, [Description("Time span.")] TimeSpan timespan, [Description("Member.")] DiscordMember member, [RemainingText, Description("Reason.")] string reason = null) { if (member.Id == ctx.User.Id) { throw new CommandFailedException("You can't ban yourself."); } await member.BanAsync(delete_message_days : 0, reason : ctx.BuildInvocationDetailsString($"(tempban for {timespan.ToString()}) " + reason)); DateTimeOffset until = DateTimeOffset.Now + timespan; await this.InformAsync(ctx, $"{Formatter.Bold(ctx.User.Username)} BANNED {Formatter.Bold(member.Username)} until {Formatter.Bold(until.ToString())} UTC!"); var task = new UnbanTaskInfo(ctx.Guild.Id, member.Id, until); await SavedTaskExecutor.ScheduleAsync(this.Shared, this.Database, (DiscordClientImpl)ctx.Client, task); }
private async Task AddReminderAsync(CommandContext ctx, TimeSpan timespan, DiscordChannel channel, string message, bool repeat = false) { if (string.IsNullOrWhiteSpace(message)) { throw new InvalidCommandUsageException("Missing time or repeat string."); } if (message.Length > 250) { throw new InvalidCommandUsageException("Message must be shorter than 250 characters."); } if (timespan.TotalMinutes < 1 || timespan.TotalDays > 31) { throw new InvalidCommandUsageException("Time span cannot be less than 1 minute or greater than 31 days."); } if (this.Shared.RemindExecuters.ContainsKey(ctx.User.Id) && this.Shared.RemindExecuters[ctx.User.Id].Count >= 20) { throw new CommandFailedException("You cannot have more than 20 reminders scheduled!"); } DateTimeOffset when = DateTimeOffset.Now + timespan; var task = new SendMessageTaskInfo(channel?.Id ?? 0, ctx.User.Id, message, when, repeat, timespan); await SavedTaskExecutor.ScheduleAsync(this.Shared, this.Database, (DiscordClientImpl)ctx.Client, task); if (repeat) { await this.InformAsync(ctx, StaticDiscordEmoji.AlarmClock, $"I will repeatedly remind {channel?.Mention ?? "you"} every {Formatter.Bold(timespan.Humanize(5))} to:\n\n{message}", important : false); } else { await this.InformAsync(ctx, StaticDiscordEmoji.AlarmClock, $"I will remind {channel?.Mention ?? "you"} in {Formatter.Bold(timespan.Humanize(5))} ({when.ToUtcTimestamp()}) to:\n\n{message}", important : false); } }
public async Task TempBanAsync(CommandContext ctx, [Description("User (doesn't have to be a member).")] DiscordUser user, [Description("Time span.")] TimeSpan timespan, [RemainingText, Description("Reason.")] string reason = null) { if (user.Id == ctx.User.Id) { throw new CommandFailedException("You can't ban yourself."); } if (timespan.TotalMinutes < 1 || timespan.TotalDays > 31) { throw new InvalidCommandUsageException("Given time period cannot be lower than 1 minute or greater than 1 month"); } await ctx.Guild.BanMemberAsync(user.Id, 0, ctx.BuildInvocationDetailsString(reason)); DateTime until = DateTime.UtcNow + timespan; await this.InformAsync(ctx, $"{ctx.Member.Mention} {Formatter.Bold("BANNED")} {user.ToString()} for {Formatter.Bold(timespan.Humanize(4, minUnit: TimeUnit.Second))}!"); var task = new UnbanTaskInfo(ctx.Guild.Id, user.Id, until); await SavedTaskExecutor.ScheduleAsync(this.Shared, this.Database, ctx.Client, task); }
public async Task TempBanAsync(CommandContext ctx, [Description("Time span.")] TimeSpan timespan, [Description("Member.")] DiscordMember member, [RemainingText, Description("Reason.")] string reason = null) { if (member.Id == ctx.User.Id) { throw new CommandFailedException("You can't ban yourself."); } if (timespan.TotalMinutes < 1 || timespan.TotalDays > 31) { throw new InvalidCommandUsageException("Given time period cannot be lower than 1 minute or greater than 1 month"); } await member.BanAsync(delete_message_days : 0, reason : ctx.BuildInvocationDetailsString($"(tempban for {timespan.ToString()}) " + reason)); var until = DateTimeOffset.Now + timespan; await this.InformAsync(ctx, $"{ctx.Member.Mention} {Formatter.Bold("BANNED")} {member.DisplayName} for {Formatter.Bold(timespan.Humanize(4, minUnit: TimeUnit.Second))}!"); var task = new UnbanTaskInfo(ctx.Guild.Id, member.Id, until); await SavedTaskExecutor.ScheduleAsync(this.Shared, this.Database, ctx.Client, task); }
private static async Task RegisterPeriodicTasksAsync() { BotStatusUpdateTimer = new Timer(BotActivityCallback, Shards[0].Client, TimeSpan.FromSeconds(10), TimeSpan.FromMinutes(10)); DatabaseSyncTimer = new Timer(DatabaseSyncCallback, Shards[0].Client, TimeSpan.FromMinutes(1), TimeSpan.FromSeconds(BotConfiguration.DatabaseSyncInterval)); FeedCheckTimer = new Timer(FeedCheckCallback, Shards[0].Client, TimeSpan.FromSeconds(BotConfiguration.FeedCheckStartDelay), TimeSpan.FromSeconds(BotConfiguration.FeedCheckInterval)); MiscActionsTimer = new Timer(MiscellaneousActionsCallback, Shards[0].Client, TimeSpan.FromSeconds(5), TimeSpan.FromHours(12)); using (DatabaseContext db = GlobalDatabaseContextBuilder.CreateContext()) { await RegisterSavedTasksAsync(db.SavedTasks.ToDictionary <DatabaseSavedTask, int, SavedTaskInfo>( t => t.Id, t => { switch (t.Type) { case SavedTaskType.Unban: return(new UnbanTaskInfo(t.GuildId, t.UserId, t.ExecutionTime)); case SavedTaskType.Unmute: return(new UnmuteTaskInfo(t.GuildId, t.UserId, t.RoleId, t.ExecutionTime)); default: return(null); } }) ); await RegisterRemindersAsync(db.Reminders.ToDictionary( t => t.Id, t => new SendMessageTaskInfo(t.ChannelId, t.UserId, t.Message, t.ExecutionTime, t.IsRepeating, t.RepeatInterval) )); } async Task RegisterSavedTasksAsync(IReadOnlyDictionary <int, SavedTaskInfo> tasks) { int scheduled = 0, missed = 0; foreach ((int tid, SavedTaskInfo task) in tasks) { if (await RegisterTaskAsync(tid, task)) { scheduled++; } else { missed++; } } SharedData.LogProvider.ElevatedLog(LogLevel.Info, $"Saved tasks: {scheduled} scheduled; {missed} missed."); } async Task RegisterRemindersAsync(IReadOnlyDictionary <int, SendMessageTaskInfo> reminders) { int scheduled = 0, missed = 0; foreach ((int tid, SendMessageTaskInfo task) in reminders) { if (await RegisterTaskAsync(tid, task)) { scheduled++; } else { missed++; } } SharedData.LogProvider.ElevatedLog(LogLevel.Info, $"Reminders: {scheduled} scheduled; {missed} missed."); } async Task <bool> RegisterTaskAsync(int id, SavedTaskInfo tinfo) { var texec = new SavedTaskExecutor(id, Shards[0].Client, tinfo, SharedData, GlobalDatabaseContextBuilder); if (texec.TaskInfo.IsExecutionTimeReached) { await texec.HandleMissedExecutionAsync(); return(false); } else { texec.Schedule(); return(true); } } }
public async Task PunishMemberAsync(DiscordGuild guild, DiscordMember member, PunishmentActionType type, TimeSpan?cooldown = null, string reason = null) { try { DiscordRole muteRole; SavedTaskInfo task; switch (type) { case PunishmentActionType.Kick: await member.RemoveAsync(reason ?? this.reason); break; case PunishmentActionType.PermanentMute: muteRole = await this.GetOrCreateMuteRoleAsync(guild); if (member.Roles.Contains(muteRole)) { return; } await member.GrantRoleAsync(muteRole, reason ?? this.reason); break; case PunishmentActionType.PermanentBan: await member.BanAsync(1, reason : reason ?? this.reason); break; case PunishmentActionType.TemporaryBan: await member.BanAsync(0, reason : reason ?? this.reason); task = new UnbanTaskInfo(guild.Id, member.Id, cooldown is null ? null : DateTimeOffset.Now + cooldown); await SavedTaskExecutor.ScheduleAsync(this.shard.SharedData, this.shard.Database, this.shard.Client, task); break; case PunishmentActionType.TemporaryMute: muteRole = await this.GetOrCreateMuteRoleAsync(guild); if (member.Roles.Contains(muteRole)) { return; } await member.GrantRoleAsync(muteRole, reason ?? this.reason); task = new UnmuteTaskInfo(guild.Id, member.Id, muteRole.Id, cooldown is null ? null : DateTimeOffset.Now + cooldown); await SavedTaskExecutor.ScheduleAsync(this.shard.SharedData, this.shard.Database, this.shard.Client, task); break; } } catch { var logchn = this.shard.SharedData.GetLogChannelForGuild(this.shard.Client, guild); if (!(logchn is null)) { var emb = new DiscordEmbedBuilder { Title = "User punish attemp failed! Check my permissions...", Color = DiscordColor.Red }; emb.AddField("User", member?.ToString() ?? "unknown", inline: true); emb.AddField("Reason", reason ?? this.reason, inline: false); await logchn.SendMessageAsync(embed : emb.Build()); } } }
private static async Task RegisterPeriodicTasksAsync() { BotStatusUpdateTimer = new Timer(BotActivityCallback, Shards[0].Client, TimeSpan.FromSeconds(10), TimeSpan.FromMinutes(10)); DatabaseSyncTimer = new Timer(DatabaseSyncCallback, Shards[0].Client, TimeSpan.FromMinutes(1), TimeSpan.FromSeconds(BotConfiguration.DatabaseSyncInterval)); FeedCheckTimer = new Timer(FeedCheckCallback, Shards[0].Client, TimeSpan.FromSeconds(BotConfiguration.FeedCheckStartDelay), TimeSpan.FromSeconds(BotConfiguration.FeedCheckInterval)); MiscActionsTimer = new Timer(MiscellaneousActionsCallback, Shards[0].Client, TimeSpan.FromSeconds(5), TimeSpan.FromHours(12)); await RegisterSavedTasks(await DatabaseService.GetAllSavedTasksAsync()); await RegisterReminders(await DatabaseService.GetAllRemindersAsync()); async Task RegisterSavedTasks(IReadOnlyDictionary <int, SavedTaskInfo> tasks) { int registeredTasks = 0, missedTasks = 0; foreach ((int tid, SavedTaskInfo task) in tasks) { if (await RegisterTask(tid, task)) { registeredTasks++; } else { missedTasks++; } } SharedData.LogProvider.ElevatedLog(LogLevel.Info, $"Saved tasks: {registeredTasks} registered; {missedTasks} missed."); } async Task RegisterReminders(IReadOnlyDictionary <int, SendMessageTaskInfo> reminders) { int registeredTasks = 0, missedTasks = 0; foreach ((int tid, SendMessageTaskInfo task) in reminders) { if (await RegisterTask(tid, task)) { registeredTasks++; } else { missedTasks++; } } SharedData.LogProvider.ElevatedLog(LogLevel.Info, $"Reminders: {registeredTasks} registered; {missedTasks} missed."); } async Task <bool> RegisterTask(int id, SavedTaskInfo tinfo) { var texec = new SavedTaskExecutor(id, Shards[0].Client, tinfo, SharedData, DatabaseService); if (texec.TaskInfo.IsExecutionTimeReached) { await texec.HandleMissedExecutionAsync(); return(false); } else { texec.Schedule(); return(true); } } }