public void PausedTimerShouldBeSaveAndLoadable()
        {
            int           waitTimeMs = 100;
            bool          elapsed    = false;
            PausableTimer timer      = new PausableTimer(waitTimeMs);

            timer.Elapsed += (a, b) =>
            {
                elapsed = true;
            };
            int margin = Mathf.RoundToInt(waitTimeMs * 0.1f);

            timer.Start();



            // Wait half the time, then save
            System.Threading.Thread.Sleep((waitTimeMs + margin) / 2);
            timer.Pause();
            IGenericSaveData save = timer.GetSave();

            Assert.IsFalse(elapsed);

            timer          = new PausableTimer(save);
            timer.Elapsed += (a, b) =>
            {
                elapsed = true;
            };
            System.Threading.Thread.Sleep((waitTimeMs + margin) / 2);
            Assert.IsFalse(elapsed);

            timer.Resume();
            System.Threading.Thread.Sleep((waitTimeMs + margin) / 2);
            Assert.IsTrue(elapsed);
        }
Пример #2
0
        private void AddSpawnTimer(int interval, Func <Enemy> createEnemy, string enemyName)
        {
            var timer = new PausableTimer(interval);

            timer.Elapsed += (sender, args) => SpawnEnemy(createEnemy, enemyName);
            _spawnTimers.Add(timer);
        }
Пример #3
0
        private void DisposeSpotifyLauncherTimeoutTimer()
        {
            if (this.spotifyLauncherTimeoutTimer != null)
            {
                this.spotifyLauncherTimeoutTimer.Enabled  = false;
                this.spotifyLauncherTimeoutTimer.Elapsed -= this.SpotifyLauncherTimeoutTimer_Elapsed;

                this.spotifyLauncherTimeoutTimer.Close();
                this.spotifyLauncherTimeoutTimer = null;
            }
        }
        public void TimerShouldWait()
        {
            int           waitTimeMs = 100;
            bool          elapsed    = false;
            PausableTimer timer      = new PausableTimer(waitTimeMs);

            timer.Elapsed += (a, b) =>
            {
                elapsed = true;
            };
            int margin = Mathf.RoundToInt(waitTimeMs * 0.1f);

            timer.Start();
            System.Threading.Thread.Sleep(waitTimeMs - margin);
            Assert.IsFalse(elapsed);
            System.Threading.Thread.Sleep(2 * margin); // is now time+margin
            Assert.IsTrue(elapsed);
        }
Пример #5
0
        public void StartSpotify()
        {
            this.spotifyLauncher = new BackgroundWorker {
                WorkerSupportsCancellation = true
            };
            this.spotifyLauncher.DoWork             += this.StartSpotify_WorkerTask;
            this.spotifyLauncher.RunWorkerCompleted += this.StartSpotify_WorkerTaskCompleted;

            if (Settings.Current.StartupWaitTimeout < 60000)
            {
                Settings.Current.StartupWaitTimeout = 60000;
            }
            this.spotifyLauncherTimeoutTimer = new PausableTimer(Settings.Current.StartupWaitTimeout)
            {
                AutoReset = false
            };
            this.spotifyLauncherTimeoutTimer.Elapsed += this.SpotifyLauncherTimeoutTimer_Elapsed;

            this.spotifyLauncher.RunWorkerAsync();
            this.spotifyLauncherTimeoutTimer.Start();
        }
Пример #6
0
        protected override void OnSettingChanged(object sender, EventArgs e)
        {
            if (_timer != null)
            {
                _timer.Stop();
                _timer.Elapsed -= OnTimerElapsed;
                _timer.Dispose();
            }

            _timer          = new PausableTimer(Setting.WebApiDownloadInterval.TotalMilliseconds);
            _timer.Elapsed += OnTimerElapsed;

            if ((sender as Setting)?.ImageBackgroundType != ImageBackgroundType.WebApi)
            {
                _timer.Stop();
                return;
            }

            ImageDownloader.ResetUrl();
            ChangeImage();
        }
Пример #7
0
        protected override void OnSettingChanged(object sender, EventArgs e)
        {
            if (_timer != null)
            {
                _timer.Stop();
                _timer.Elapsed -= OnTimerElapsed;
                _timer.Dispose();
            }
            _timer          = new PausableTimer(Setting.UpdateImageInterval.TotalMilliseconds);
            _timer.Elapsed += OnTimerElapsed;

            if ((sender as Setting)?.ImageBackgroundType != ImageBackgroundType.Slideshow)
            {
                _timer.Stop();
            }
            else
            {
                _imageFiles     = GetImagesFromDirectory();
                _imageFilesPath = _imageFiles.GetEnumerator();
                ChangeImage();
                _timer.Restart();
            }
        }
        public void TimerShouldBePausable()
        {
            int           waitTimeMs = 100;
            bool          elapsed    = false;
            PausableTimer timer      = new PausableTimer(waitTimeMs);

            timer.Elapsed += (a, b) =>
            {
                elapsed = true;
            };
            int margin = waitTimeMs / 10;

            timer.Start();
            timer.Pause();
            System.Threading.Thread.Sleep(waitTimeMs + margin);
            Assert.IsFalse(elapsed);

            timer.Resume();
            System.Threading.Thread.Sleep(waitTimeMs - margin);
            Assert.IsFalse(elapsed);
            System.Threading.Thread.Sleep(2 * margin); // is now time+margin
            Assert.IsTrue(elapsed);
        }
Пример #9
0
        /// <summary>
        /// Waits any text answer
        /// </summary>
        /// <param name="channel">A <see cref="DiscordChannel"/> where message should be</param>
        /// <param name="messageBody">A body of this message</param>
        /// <param name="validateAnswer">returns true if an answer is acceptable, or false to throw error message</param>
        /// <param name="onValidatedAnswer">Do any demanding task here to process this answer</param>
        /// <param name="UserId">In case one user should answer this question</param>
        /// <param name="behavior">What to do after timeout</param>
        /// <param name="timeout">This message will be terminated after timeout</param>
        /// <param name="timeoutMessage"></param>
        /// <param name="showTimeoutMessage"></param>
        /// <param name="timeoutBeforeDelete"></param>
        /// <param name="wrongAnswer"></param>
        /// <param name="existingMessage">Existing <see cref="DiscordMessage"/> to modify and to subscribe to</param>
        /// <param name="deleteAnswer"></param>
        /// <param name="deleteAnswerTimeout"></param>
        /// <param name="waitForMultipleAnswers">If <see langword="false"/> it will unsubscribe after getting first answer</param>
        public static async Task CreateQuestion(DiscordChannel channel, string messageBody,
                                                Func <string, bool> validateAnswer, Func <Answer.AnswerArgs, Task> onValidatedAnswer,
                                                ulong?UserId                   = null, MessageBehavior behavior = MessageBehavior.Volatile,
                                                TimeSpan?timeout               = null, string timeoutMessage    = "Timeout", bool showTimeoutMessage = true,
                                                TimeSpan?timeoutBeforeDelete   = null, string wrongAnswer       = "This isn't what I'm looking for.",
                                                DiscordMessage existingMessage = null, bool deleteAnswer        = false, TimeSpan?deleteAnswerTimeout = null, bool waitForMultipleAnswers = false)
        {
            SetDefaultValuesForTimeouts(ref timeout, ref timeoutBeforeDelete, ref deleteAnswerTimeout);
            DiscordMessage Question = await GetOrCreateMessage(channel, messageBody, existingMessage).ConfigureAwait(false);

            bool answered     = false;
            bool unsubscribed = false;

            PausableTimer timeoutTimer = new PausableTimer(timeout.Value.TotalMilliseconds);

            timeoutTimer.AutoReset = false;

            timeoutTimer.Elapsed += async(s, e) => {
                //Do we need to dispose it? Or just leave it?
                //In case of permanent behavior it will just skip and won't unsubscribe, just what we need
                timeoutTimer.Stop();
                if (behavior != MessageBehavior.Permanent && !answered)
                {
                    _ = UnsubscribeAndDismiss();
                    //Timeout message
                    if ((!answered || waitForMultipleAnswers) && showTimeoutMessage)
                    {
                        await QuickVolatileMessage(channel, timeoutMessage, TimeSpan.FromSeconds(3)).ConfigureAwait(false);
                    }
                }
            };

            Bot.Instance.Client.MessageCreated += MessageAdded;
            Bot.Instance.Client.MessageDeleted += OnDeleted;
            //Do I need to even start it in case of Permanent behavior?
            timeoutTimer.Start();

            //This will hold this method from returning until it will receive right answer or get deleted
            //This will give an opportunity to nest dialogs one after another
            while (!unsubscribed)
            {
                await Task.Delay(100);
            }

            Task MessageAdded(MessageCreateEventArgs e)
            {
                //Ignore other channels and other users activity
                if (e.Channel.Id != Question.ChannelId || e.Author.IsCurrent || UserId.HasValue ? e.Author.Id != UserId.Value : false)
                {
                    return(Task.CompletedTask);
                }
                timeoutTimer.Pause();
                //save answered state for further use, but keep local copy for this event, it can be changed outside
                var a = answered = validateAnswer(e.Message.Content);

                if (a && !waitForMultipleAnswers)
                {
                    timeoutTimer.Stop();
                    _ = UnsubscribeAndDismiss();
                }
                if (a)
                {
                    _ = onValidatedAnswer(new Answer.AnswerArgs(e.Author, true, e.Message, Question));
                }
                else
                {
                    //wrong answer
                    _ = QuickVolatileMessage(channel, wrongAnswer, timeoutBeforeDelete);
                }

                if (deleteAnswer)
                {
                    _ = DeleteMessageAfter(e.Message, deleteAnswerTimeout.Value.Milliseconds).ConfigureAwait(false);
                }
                if (waitForMultipleAnswers || !answered)
                {
                    timeoutTimer.Release();
                }
                return(Task.CompletedTask);               //Nice hack, thank's VS
            }

            async Task UnsubscribeAndDismiss()
            {
                if (unsubscribed)
                {
                    return;
                }
                unsubscribed = true;
                if (behavior != MessageBehavior.Permanent)
                {
                    Bot.Instance.Client.MessageCreated -= MessageAdded;
                }
                if (behavior == MessageBehavior.Volatile)
                {
                    //Should pause here
                    await Task.Delay((int)timeoutBeforeDelete.Value.TotalMilliseconds).ConfigureAwait(false);

                    await Question.DeleteAsync().ConfigureAwait(false);
                }
            }

            Task OnDeleted(MessageDeleteEventArgs e)
            {
                if (e.Message.Id != Question.Id)
                {
                    return(Task.CompletedTask);
                }
                unsubscribed = true;
                Bot.Instance.Client.MessageDeleted -= OnDeleted;
                Bot.Instance.Client.MessageCreated -= MessageAdded;
                return(Task.CompletedTask);
            }
        }
Пример #10
0
        /// <summary>
        /// Will create a message on a given channel, or will find an existing in case of restoring a previous session,
        /// apply answer emojis and subscribe to emojis and text answers
        /// </summary>
        /// <param name="channel">A <see cref="DiscordChannel"/> where message should be</param>
        /// <param name="messageBody">A body of this message</param>
        /// <param name="answers">List of structs containing answers in form of emojis to click or written answers with callbacks</param>
        /// <param name="UserId">In case one user should answer this question</param>
        /// <param name="timeout">This message will be terminated after timeout</param>
        /// <param name="behaviour">What to do after timeout</param>
        /// <param name="existingMessageId">If there is a message already to subscribe to</param>
        /// <param name="deleteAnswer">Delete reaction rightaway for anonymosity or to use it as a button</param>
        /// <param name="waitForMultipleAnswers">If <see langword="false"/> it will unsubscribe after getting first answer</param>
        /// <returns></returns>
        public static async Task CreateMessage(DiscordChannel channel, string messageBody,
                                               IEnumerable <Answer> answers, ulong?UserId = null, MessageBehavior behaviour = MessageBehavior.Volatile,
                                               TimeSpan?timeout               = null, string timeoutMessage = "Timed out", bool showTimeoutMessage = true,
                                               TimeSpan?timeoutBeforeDelete   = null, string wrongAnswer    = "This isn't what I'm looking for. Acceptable keywords are: {0}",
                                               DiscordMessage existingMessage = null, bool deleteAnswer     = false, TimeSpan?deleteAnswerTimeout = null, bool waitForMultipleAnswers = false)
        {
            SetDefaultValuesForTimeouts(ref timeout, ref timeoutBeforeDelete, ref deleteAnswerTimeout);

            //Find or create a message even if missing
            DiscordMessage DMessage = await GetOrCreateMessage(channel, messageBody, existingMessage).ConfigureAwait(false);

            await SetReactions(DMessage, answers.Where(a => a.Emoji != null), true).ConfigureAwait(false);

            bool answered     = false;
            bool unsubscribed = false;
            //Should this do? It's ether suppress CS1998 or do this. Unless this will have some unpredictable result. Anyway, this Task should be .ConfigureAwait(false)
            Func <Task>   unsubscribeAndDismiss = async() => { await Task.Yield(); };
            PausableTimer timeoutTimer          = new PausableTimer(timeout.Value.TotalMilliseconds);

            timeoutTimer.AutoReset = false;

            //Some optimizations
            var answersFromTokens   = answers.SelectMany(a => a.StringTokens.Select(t => (Answer: a, Token: t))).ToArray();
            var answersFromEmojiIds = answers.ToDictionary(a => a.Emoji);

            if (answers.Count() > 0)
            {
                var reactionAdded = new DSharpPlus.AsyncEventHandler <DSharpPlus.EventArgs.MessageReactionAddEventArgs>(async(e) => {
                    //Ignore own reactions and other messages
                    if (e.User.IsCurrent || e.Message.Id != DMessage.Id)
                    {
                        return;
                    }
                    //TODO Findout what if it didn't find? Or use foreach, like in messages
                    if (deleteAnswer)
                    {
                        await((Func <Task>)(async() => {
                            await Task.Delay((int)deleteAnswerTimeout.Value.TotalMilliseconds);
                            await e.Message.DeleteReactionAsync(e.Emoji, e.User);
                        }))().ConfigureAwait(false);
                    }

                    if (answersFromEmojiIds.ContainsKey(e.Emoji))
                    {
                        var answer = answersFromEmojiIds[e.Emoji];

                        timeoutTimer.Pause();

                        //Do we need to check if there is another answer set already, just in case of hickup
                        //TODO It's a reaction answer. Shouldn't it be true by default?
                        answered = true;
                        _        = answer.InvokeFromEmoji(e.User, DMessage);
                    }

                    if (!waitForMultipleAnswers && answered)
                    {
                        timeoutTimer.Stop();
                        timeoutTimer.Dispose();
                        await unsubscribeAndDismiss().ConfigureAwait(false);
                        return;
                    }
                    else
                    {
                        timeoutTimer.Release();
                    }
                });
                var messageAdded = new DSharpPlus.AsyncEventHandler <DSharpPlus.EventArgs.MessageCreateEventArgs>(async e => {
                    //Ignore other channels and other users activity
                    if (e.Channel.Id != DMessage.ChannelId || e.Author.IsCurrent || UserId.HasValue ? e.Author.Id != UserId.Value : false)
                    {
                        return;
                    }

                    //Get list of all tokens ans Answer objects
                    foreach (var pair in answersFromTokens)
                    {
                        if (e.Message.Content.ToLower().Contains(pair.Token))
                        {
                            timeoutTimer.Pause();
                            //???Do we really need to get answer value if we already found a token?
                            answered = pair.Answer.ValidateAnswer(e.Message.Content);
                            _        = pair.Answer.InvokeFromMessage(e.Message, DMessage);
                            if (deleteAnswer)
                            {
                                //Doing it afterwards is simpler
                                await e.Message.DeleteAsync().ConfigureAwait(false);
                            }

                            //what to do if answer wasn't accepted? should be handled internally? Quick response

                            if (!waitForMultipleAnswers && answered)
                            {
                                timeoutTimer.Stop();
                                timeoutTimer.Dispose();
                                await unsubscribeAndDismiss().ConfigureAwait(false);
                                return;
                            }
                            else
                            {
                                timeoutTimer.Release();
                            }
                        }
                    }
                    //Seems like answer wasn't recognised. Should it have an event to expire? Or make a new method for that?

                    await QuickVolatileMessage(channel, string.Format(wrongAnswer,
                                                                      string.Join(", ", answers.Where(a => a.StringTokens.Length > 0).SelectMany(a => a.StringTokens))),
                                               TimeSpan.FromSeconds(10)).ConfigureAwait(false);
                });
                unsubscribeAndDismiss = async() => {
                    //Should we also empty this action? This one will be ready to be collected...
                    if (unsubscribed)
                    {
                        return;
                    }
                    if (behaviour == MessageBehavior.Volatile || behaviour == MessageBehavior.KeepMessageAfterTimeout)
                    {
                        //unsubscribe later//TODO Check whether it actually works
                        if (answers.Any(a => a.Emoji != null))
                        {
                            Bot.Instance.Client.MessageReactionAdded -= reactionAdded;
                        }
                        if (answers.Any(answers => answers.StringTokens.Length > 0))
                        {
                            Bot.Instance.Client.MessageCreated -= messageAdded;
                        }
                    }
                    if (behaviour == MessageBehavior.Volatile)
                    {
                        //Should pause here
                        await Task.Delay((int)timeoutBeforeDelete.Value.TotalMilliseconds);

                        await DMessage.DeleteAsync().ConfigureAwait(false);
                    }
                    else if (behaviour == MessageBehavior.KeepMessageAfterTimeout)
                    {
                        //TODO should we delete only bots reactions?
                        await DMessage.DeleteAllReactionsAsync();
                    }
                };
                //Subscribe to reactions and answers

                if (answers.Any(a => a.Emoji != null))
                {
                    Bot.Instance.Client.MessageReactionAdded += reactionAdded;
                }
                if (answers.Any(answers => answers.StringTokens.Length > 0))
                {
                    Bot.Instance.Client.MessageCreated += messageAdded;
                }
            }
            timeoutTimer.Start();

            timeoutTimer.Elapsed += async(s, e) => {
                //Do we need to dispose it? Or just leave it?
                //In case of permanent behavior it will just skip and won't unsubscribe, just what we need
                timeoutTimer.Stop();
                if (behaviour != MessageBehavior.Permanent && !answered)
                {
                    await unsubscribeAndDismiss().ConfigureAwait(false);

                    //Timeout message
                    if (!answered && showTimeoutMessage)
                    {
                        await QuickVolatileMessage(channel, timeoutMessage, TimeSpan.FromSeconds(3)).ConfigureAwait(false);
                    }
                }
            };
        }
Пример #11
0
        public static async Task <DiscordMessage> CreateQuestion(DiscordChannel channel, string messageBody, IEnumerable <Answer> answers,
                                                                 ulong?UserId                   = null, MessageBehavior behavior = MessageBehavior.Volatile,
                                                                 TimeSpan?timeout               = null, string timeoutMessage    = "Timeout", bool showTimeoutMessage = true,
                                                                 TimeSpan?timeoutBeforeDelete   = null, string wrongAnswer       = "This isn't what I'm looking for.",
                                                                 DiscordMessage existingMessage = null, bool deleteAnswer        = false, TimeSpan?deleteAnswerTimeout = null, bool waitForMultipleAnswers = false)
        {
            SetDefaultValuesForTimeouts(ref timeout, ref timeoutBeforeDelete, ref deleteAnswerTimeout);
            DiscordMessage Question = await GetOrCreateMessage(channel, messageBody, existingMessage).ConfigureAwait(false);

            //Some optimizations
            var answersFromTokens   = answers.SelectMany(a => a.StringTokens.Select(t => (Answer: a, Token: t))).ToArray();
            var answersFromEmojiIds = answers.ToDictionary(a => a.Emoji);

            _ = SetReactions(Question, answers.Where(a => a.Emoji != null), true);

            bool answered     = false;
            bool unsubscribed = false;

            PausableTimer timeoutTimer = new PausableTimer(timeout.Value.TotalMilliseconds);

            timeoutTimer.AutoReset = false;

            timeoutTimer.Elapsed += async(s, e) => {
                //Do we need to dispose it? Or just leave it?
                //In case of permanent behavior it will just skip and won't unsubscribe, just what we need
                timeoutTimer.Stop();
                if (behavior != MessageBehavior.Permanent && !answered)
                {
                    _ = UnsubscribeAndDismiss();
                    //Timeout message
                    if ((!answered || waitForMultipleAnswers) && showTimeoutMessage)
                    {
                        await QuickVolatileMessage(channel, timeoutMessage, TimeSpan.FromSeconds(3)).ConfigureAwait(false);
                    }
                }
            };

            if (answersFromEmojiIds.Count > 0)
            {
                Bot.Instance.Client.MessageReactionAdded += ReactionAdded;
            }
            if (answersFromTokens.Count() > 0)
            {
                Bot.Instance.Client.MessageCreated += MessageAdded;
            }
            Bot.Instance.Client.MessageDeleted += OnDeleted;
            //Do I need to even start it in case of Permanent behavior?
            timeoutTimer.Start();

            //This will hold this method from returning until it will receive right answer or get deleted
            //This will give an opportunity to nest dialogs one after another
            while (!unsubscribed && behavior != MessageBehavior.Permanent)
            {
                await Task.Delay(100);
            }
            //Permanent fall through and return it's message object
            return(Question);


            async Task ReactionAdded(MessageReactionAddEventArgs e)
            {
                //Ignore own reactions and other messages
                if (e.User.IsCurrent || e.Message.Id != Question.Id)
                {
                    return;
                }

                if (deleteAnswer)
                {
                    await((Func <Task>)(async() => {
                        await Task.Delay((int)deleteAnswerTimeout.Value.TotalMilliseconds);
                        await e.Message.DeleteReactionAsync(e.Emoji, e.User);
                    }))().ConfigureAwait(false);
                }

                if (answersFromEmojiIds.ContainsKey(e.Emoji))
                {
                    var answer = answersFromEmojiIds[e.Emoji];

                    timeoutTimer.Pause();

                    answered = true;
                    _        = answer.InvokeFromEmoji(e.User, Question);
                }

                //There is a case when you need to keep it working, like a reaction role thing
                if (!waitForMultipleAnswers && answered && behavior != MessageBehavior.Permanent)
                {
                    timeoutTimer.Stop();
                    timeoutTimer.Dispose();
                    await UnsubscribeAndDismiss().ConfigureAwait(false);

                    return;
                }
                else
                {
                    timeoutTimer.Release();
                }
            }

            async Task MessageAdded(MessageCreateEventArgs e)
            {
                //Ignore other channels and other users activity
                if (e.Channel.Id != Question.ChannelId || e.Author.IsCurrent || UserId.HasValue ? e.Author.Id != UserId.Value : false)
                {
                    return;
                }

                //Get list of all tokens ans Answer objects
                foreach (var pair in answersFromTokens)
                {
                    if (e.Message.Content.ToLower().Contains(pair.Token))
                    {
                        timeoutTimer.Pause();

                        //If there is a blank token, we will need a validation
                        answered = pair.Answer.ValidateAnswer(e.Message.Content);
                        _        = pair.Answer.InvokeFromMessage(e.Message, Question);
                        if (deleteAnswer)
                        {
                            //Doing it afterwards is simpler
                            await e.Message.DeleteAsync().ConfigureAwait(false);
                        }

                        //what to do if answer wasn't accepted? should be handled internally? Quick response

                        if (!waitForMultipleAnswers && answered)
                        {
                            timeoutTimer.Stop();
                            timeoutTimer.Dispose();
                            await UnsubscribeAndDismiss().ConfigureAwait(false);

                            return;
                        }
                        else
                        {
                            timeoutTimer.Release();
                        }
                    }
                }
                //Seems like answer wasn't recognised. Should it have an event to expire? Or make a new method for that?

                if (!answered)
                {
                    await QuickVolatileMessage(channel, string.Format(wrongAnswer,
                                                                      string.Join(", ", answers.Where(a => a.StringTokens.Length > 0).SelectMany(a => a.StringTokens))),
                                               TimeSpan.FromSeconds(10)).ConfigureAwait(false);
                }
            }

            async Task UnsubscribeAndDismiss()
            {
                if (unsubscribed)
                {
                    return;
                }
                unsubscribed = true;
                if (behavior != MessageBehavior.Permanent)
                {
                    //Can I unsubscribe if not even subscribed?
                    Bot.Instance.Client.MessageReactionAdded -= ReactionAdded;
                    Bot.Instance.Client.MessageCreated       -= MessageAdded;
                }
                if (behavior == MessageBehavior.Volatile)
                {
                    //Should pause here
                    await Task.Delay((int)timeoutBeforeDelete.Value.TotalMilliseconds).ConfigureAwait(false);

                    await Question.DeleteAsync().ConfigureAwait(false);
                }
            }

            Task OnDeleted(MessageDeleteEventArgs e)
            {
                if (e.Message.Id != Question.Id)
                {
                    return(Task.CompletedTask);
                }
                unsubscribed = true;
                Bot.Instance.Client.MessageDeleted -= OnDeleted;
                Bot.Instance.Client.MessageCreated -= MessageAdded;
                return(Task.CompletedTask);
            }
        }