/// <summary>
        /// Adds new post or updates the existing post in the raid post array.
        /// Matches existing posts with the following logic:
        ///     - Message in the same original channel
        ///     - Pokemon name matches (first priority)
        ///     - User already in thread (second priority)
        /// </summary>
        /// <param name="post"></param>
        /// <returns>Returns either the new post or the one it matched with.</returns>
        public PokemonRaidPost AddPost(PokemonRaidPost post, MessageParser parser, IChatMessage message, bool add = true, bool force = false)
        {
            var             guildConfig  = Config.GetServerConfig(post.GuildId, ChatTypes.Discord);
            var             channelPosts = guildConfig.Posts.Where(x => x.ChannelMessages.Keys.Where(xx => post.ChannelMessages.Keys.Contains(xx)).Count() > 0);
            PokemonRaidPost existing     = null;

            if (!force)
            {
                //Check if user in existing post is mentioned, get latest post associated with user
                foreach (var mentionedUser in message.MentionedUsers)
                {
                    existing = channelPosts.OrderByDescending(x => x.Responses.Max(xx => xx.MessageDate))
                               .FirstOrDefault(x => x.ChannelMessages.Keys.Contains(message.Channel.Id) &&
                                               (x.Responses.Where(xx => xx.UserId == mentionedUser.Id).Count() > 0 || x.JoinedUsers.Where(xx => xx.Id == mentionedUser.Id).Count() > 0) &&
                                               x.PokemonId == (post.PokemonId == 0 ? x.PokemonId : post.PokemonId) &&
                                               (string.IsNullOrWhiteSpace(x.Location) || string.IsNullOrWhiteSpace(post.Location) || parser.CompareLocationStrings(x.Location, post.Location)));

                    if (existing != null)
                    {
                        break;
                    }
                }
                //if location matches, must be same.
                if (existing == null)
                {
                    existing = channelPosts.FirstOrDefault(x => x.PokemonId == (post.PokemonId > 0 ? post.PokemonId : x.PokemonId) &&
                                                           parser.CompareLocationStrings(x.Location, post.Location));
                }

                //Lat long comparison, within 30 meters is treated as same
                //if(existing == null && post.LatLong != null && post.LatLong.HasValue && channelPosts.Where(x => x.LatLong != null && x.LatLong.HasValue).Count() > 0)
                //    existing = channelPosts.Where(x => x.LatLong != null && x.LatLong.HasValue && x.PokemonId == (post.PokemonId > 0 ? post.PokemonId : x.PokemonId))
                //        .FirstOrDefault(x => parser.CompareLocationLatLong(x.LatLong, post.LatLong));

                //Seeing if location and pokemon matches another channel's
                if (existing == null && !string.IsNullOrEmpty(post.Location))
                {
                    existing = guildConfig.Posts.FirstOrDefault(x => x.PokemonId == post.PokemonId &&
                                                                (parser.CompareLocationStrings(x.Location, post.Location) /* || parser.CompareLocationLatLong(x.LatLong, post.LatLong)*/));
                }

                //Final fall through, gets latest post in channel either matching pokemon name or user was involved with
                if (existing == null && string.IsNullOrEmpty(post.Location))//if location exists and doesn't match, not a match
                {
                    existing = channelPosts
                               .Where(x => string.IsNullOrWhiteSpace(post.Location) || x.UserId == post.UserId)
                               .OrderByDescending(x => x.PostDate)
                               .OrderBy(x => x.PokemonId == post.PokemonId ? 0 : 1)//pokemon name match takes priority if the user responded to multiple raids in the channel
                               .FirstOrDefault(x =>
                                               x.ChannelMessages.Keys.Intersect(post.ChannelMessages.Keys).Count() > 0 &&//Posted in the same channel
                                               ((post.PokemonId != default(int) && x.PokemonId == post.PokemonId) ||//Either pokemon matches OR
                                                (post.PokemonId == default(int) && (x.Responses.Where(xx => xx.UserId == post.UserId).Count() > 0) ||
                                                 post.PokemonId == default(int) && x.JoinedUsers.Where(xx => xx.Id == post.UserId).Count() > 0))//User already in the thread
                                               );
                }
            }

            if (existing != null)
            {
                if (add)
                {
                    existing = MergePosts(existing, post);
                }
                existing.IsValid = existing.IsExisting = true;
                return(existing);
            }
            else if (add && post.IsValid)
            {
                post.JoinedUsersChanged += JoinCount_Changed;
                guildConfig.Posts.Add(post);
            }

            return(post);
        }