/*/// <summary>
  * /// Called when a command is logged. Checks if a async command threw an exception.
  * /// </summary>
  * /// <param name="msg">The log message.</param>
  * private Task OnLogAsync(LogMessage msg) {
  *      if (msg.Exception is CommandException ex && IsCommandAsync(ex.Command)) {
  *              // TODO: Temp fix until async exceptions are supported
  *              // We found a valid result to a command.
  *              return commandResultEvent.InvokeAsync(ex.Command, ex.Context, ExecuteResult.FromError(ex.InnerException));
  *      }
  *      return Task.FromResult<object>(null);
  * }
  * /// <summary>
  * /// Called when a command has successfully executed.
  * /// </summary>
  * /// <param name="cmd">The command that finished executing.</param>
  * /// <param name="context">The context of the command execution.</param>
  * /// <param name="result">The execution result.</param>
  * private Task OnCommandExecutedAsync(Optional<CommandInfo> cmd, ICommandContext context, IResult result) {
  *      // TODO: Temp fix until async exceptions are supported
  *      return commandResultEvent.InvokeAsync(cmd.IsSpecified ? cmd.Value : null, context, result);
  * }*/
 /// <summary>
 /// Called when a user wait context expires. Removes it afterwards.
 /// </summary>
 /// <param name="wait">The expiring wait context.</param>
 private async Task OnWaitContextExpireAsync(IUserWaitContext wait)
 {
     if (RemoveWaitContext(wait))
     {
         wait.ExpireTimer?.Dispose();
         await wait.ExpireAsync().ConfigureAwait(false);
     }
 }
 /// <summary>
 /// Tries to remove the wait context for the user.
 /// </summary>
 /// <param name="wait">The wait context to remove.</param>
 /// <returns>
 /// True if the wait context contained was the same as <paramref name="wait"/> and was removed.
 /// </returns>
 public bool RemoveWaitContext(IUserWaitContext wait)
 {
     // Lock the wait context to prevent other actions on it during the process.
     lock (wait) {
         if (userWaitContexts.TryRemoveValue(wait.User.Id, wait))
         {
             wait.ExpireTimer?.Dispose();
             return(true);
         }
     }
     return(false);
 }
        /// <summary>
        /// Called when a message is recieved to send to a wait context.
        /// </summary>
        /// <param name="msg"></param>
        /// <returns></returns>
        private async Task OnMessageReceivedAsync(SocketMessage msg)
        {
            if (!(msg is SocketUserMessage message))
            {
                return;
            }

            IUserWaitContext waitContext = GetWaitContext(msg.Author.Id);

            if (waitContext != null)
            {
                await waitContext.MessageReceiveAsync(message).ConfigureAwait(false);
            }
        }
 /// <summary>
 /// Tries to add the wait context for the user.
 /// </summary>
 /// <param name="wait">The wait context to add.</param>
 /// <returns>True if the wait context was added.</returns>
 public void AddWaitContext(IUserWaitContext wait)
 {
     // Lock the wait context to prevent other actions on it during the process.
     lock (wait) {
         IUserWaitContext existingWait = userWaitContexts.GetOrAdd(wait.User.Id, wait);
         if (existingWait != wait)
         {
             userWaitContexts.TryRemove(wait.User.Id, out _);
             existingWait.OutputChannel.SendMessageAsync(existingWait.OverwriteMessage).GetAwaiter().GetResult();
         }
         userWaitContexts.TryAdd(wait.User.Id, wait);
         wait.ExpireTimer = new Timer((o) => OnWaitContextExpireAsync((IUserWaitContext)o).GetAwaiter().GetResult(),
                                      wait, wait.Duration, Timeout.InfiniteTimeSpan);
     }
 }