Example #1
0
        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);
            }
        }
Example #2
0
        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);
        }
Example #3
0
        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);
            }
        }
Example #4
0
        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);
            }
        }
Example #5
0
        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);
        }
Example #6
0
        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);
        }
Example #7
0
        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);
            }
        }
Example #8
0
        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);
        }
Example #9
0
        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);
        }
Example #10
0
        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);
                }
            }
        }
Example #11
0
        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());
                }
            }
        }
Example #12
0
        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);
                }
            }
        }