public static void Init()
        {
            var redis = RedisContext.Database;

            using (var context = new DatabaseContext())
            {
                redis.KeyDelete(new RedisKey[]
                    {
                        RedisContext.CurrentRoundID,
                        RedisContext.CurrentRoundKey,
                        RedisContext.UserPointRank
                    }
                );

                int roundID = context.RoundResults.Count();
                redis.StringSet(RedisContext.CurrentRoundID, roundID);

                List<SortedSetEntry> entries = new List<SortedSetEntry>();
                foreach (var user in context.Users)
                {
                    entries.Add(new SortedSetEntry(user.UserID, user.Point));
                }
                redis.SortedSetAdd(RedisContext.UserPointRank, entries.ToArray());
            }
        }
            public void ProcessNewTweet(string line)
            {
                JObject tweet = JObject.Parse(line);

                // 트윗 삭제는 무시
                if (tweet["delete"] != null)
                {
                    return;
                }

                string text = tweet["text"].ToString();
                string timeString = tweet["timestamp_ms"].ToString();
                DateTime now = new DateTime(1970, 1, 1, 0, 0, 0, 0);
                now = now.AddSeconds(UInt64.Parse(timeString) / 1000);
                using (var context = new DatabaseContext())
                {
                    RoundResult lastResult = null;
                    try
                    {
                        lastResult = context.RoundResults.LastOrDefault();
                    }
                    catch
                    {
                        // Do nothing
                    }
                    int newRoundID = 1;
                    if (lastResult != null)
                    {
                        if (now - lastResult.TweetTime < Constants.MinimumDelay)
                        {
                            return;
                        }
                        newRoundID = lastResult.RoundID + 1;
                    }

                    MessageReceivingEndpoint endpoint = new MessageReceivingEndpoint(
                        "https://api.twitter.com/1.1/statuses/oembed.json",
                        HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.AuthorizationHeaderRequest);

                    var extraData = new Dictionary<string, string>
                    {
                        { "id", tweet["id_str"].ToString() }
                    };
                    var request = consumer.PrepareAuthorizedRequest(endpoint, Constants.TwitterAccessToken, extraData);

                    RoundResult newResult = new RoundResult();
                    newResult.RoundID = newRoundID;
                    newResult.Text = text;
                    newResult.EmbedTweet = JObject.Parse(new StreamReader(request.GetResponse().GetResponseStream()).ReadToEnd())["html"].ToString();;
                    newResult.TweetTime = now;

                    var redis = RedisContext.Database;
                    redis.StringSet(RedisContext.CurrentRoundID, newRoundID);
                    var keywords = redis.SortedSetRangeByRankWithScores(RedisContext.CurrentRoundKey);
                    KeyValuePair<string, int>? specialBet = null;
                    int matchedPoint = 0, unmatchedPoint = 0;
                    foreach (var keyword in keywords)
                    {
                        var pair = new KeyValuePair<string, int>(keyword.Element.ToString(), (int)keyword.Score);
                        if (pair.Key.Length == 0)
                        {
                            specialBet = pair;
                            continue;
                        }

                        if (text.Contains(pair.Key))
                        {
                            newResult.MatchedValues.Add(pair);
                            matchedPoint += pair.Value;
                        }
                        else
                        {
                            newResult.UnmatchedValues.Add(pair);
                            unmatchedPoint += pair.Value;
                        }
                    }

                    if (specialBet != null)
                    {
                        if (newResult.MatchedValues.Count == 0)
                        {
                            newResult.MatchedValues.Add(specialBet.Value);
                            matchedPoint += specialBet.Value.Value;
                        }
                        else
                        {
                            newResult.UnmatchedValues.Add(specialBet.Value);
                            unmatchedPoint += specialBet.Value.Value;
                        }
                    }

                    context.RoundResults.Add(newResult);
                    context.SaveChanges();
                }
            }
        public async Task<dynamic> BettingKeyword(dynamic param, CancellationToken ct)
        {
            bool result = false;
            string userID = Session["UserID"].ToString();

            if (userID != null)
            {
                dynamic data = Request.Form;
                if (data.Count == 0)
                {
                    data = Request.Query;
                }
                string keyword = (data["keyword"].ToString()).Trim();
                int point = int.Parse(data["point"].ToString());

                // Special case.
                if (keyword.Length == 0)
                {
                    // ...
                }
                else
                {
                    int totalLength = keyword.Length;
                    int whiteSpaceCount = keyword.Count(c => char.IsWhiteSpace(c));
                    if ((whiteSpaceCount != 0 || totalLength < 2) && (totalLength - whiteSpaceCount) < 3)
                    {
                        return JsonConvert.SerializeObject(new Dictionary<string, object>
                            {
                                { "result", false },
                                { "reason", "Too short" }
                            }
                        );
                    }
                }

                int roundID = int.Parse(RedisContext.Database.StringGet(RedisContext.CurrentRoundID));

                using (var context = new DatabaseContext())
                {
                    await RedisContext.Database.SortedSetIncrementAsync(RedisContext.CurrentRoundKey, keyword, 1.0);
                    await RedisContext.Database.StringIncrementAsync(RedisContext.CurrentRoundPoint, point);

                    var userInfo = (from user in context.Users
                                                  where user.UserID.Equals(userID)
                                                  select user).First();
                    userInfo.Point -= point;

                    UserRound roundInfo = (from round in context.UserRounds
                                                          where round.Owner == userInfo && round.RoundID == roundID
                                                          select round).FirstOrDefault();
                    if (roundInfo == null)
                    {
                        roundInfo = new UserRound
                        {
                                Owner = userInfo,
                                RoundID = roundID
                        };
                    }

                    long count = await RedisContext.Database.ListLeftPushAsync(RedisContext.TrendKeywordList, keyword);
                    if (count > 5)
                    {
                        RedisContext.Database.ListRightPopAsync(RedisContext.TrendKeywordList);
                    }

                    if (roundInfo.BettedWords.ContainsKey(keyword))
                    {
                        roundInfo.BettedWords[keyword] += point;
                    }
                    else
                    {
                        roundInfo.BettedWords.Add(keyword, point);
                        await RedisContext.Database.StringIncrementAsync(RedisContext.CurrentRoundBettingCount);
                    }

                    await context.SaveChangesAsync();

                    result = true;
                }
            }

            return JsonConvert.SerializeObject(new Dictionary<string, object>
                {
                    { "result", result }
                }
            );
        }