public async Task persists_successfully()
        {
            SubscriptionLogRepo    repo               = new(CreateTemporaryDatabase());
            Instant                timestamp          = Instant.FromUnixTimeSeconds(123);
            const string           userId             = "123";
            const int              monthsStreak       = 101;
            const int              monthsNumPrev      = 103;
            const int              monthsNumNew       = 105;
            const int              monthsDifference   = 2;
            const int              loyaltyLeaguePrev  = 20;
            const int              loyaltyLeagueNew   = 21;
            const int              loyaltyCompletions = 1;
            const int              rewardTokens       = 10;
            const string           subMessage         = "message text";
            const SubscriptionTier subPlan            = SubscriptionTier.Tier2;
            const string           subPlanName        = "plan name";

            // persist to db
            SubscriptionLog written = await repo.LogSubscription(userId, timestamp,
                                                                 monthsStreak, monthsNumPrev, monthsNumNew, monthsDifference,
                                                                 loyaltyLeaguePrev, loyaltyLeagueNew, loyaltyCompletions, rewardTokens,
                                                                 subMessage, subPlan, subPlanName);

            Assert.That(written.Timestamp, Is.EqualTo(timestamp));
            Assert.That(written.UserId, Is.EqualTo(userId));
            Assert.That(written.MonthsStreak, Is.EqualTo(monthsStreak));
            Assert.That(written.MonthsNumPrev, Is.EqualTo(monthsNumPrev));
            Assert.That(written.MonthsNumNew, Is.EqualTo(monthsNumNew));
            Assert.That(written.MonthsDifference, Is.EqualTo(monthsDifference));
            Assert.That(written.LoyaltyLeaguePrev, Is.EqualTo(loyaltyLeaguePrev));
            Assert.That(written.LoyaltyLeagueNew, Is.EqualTo(loyaltyLeagueNew));
            Assert.That(written.LoyaltyCompletions, Is.EqualTo(loyaltyCompletions));
            Assert.That(written.RewardTokens, Is.EqualTo(rewardTokens));
            Assert.That(written.SubMessage, Is.EqualTo(subMessage));
            Assert.That(written.SubPlan, Is.EqualTo(subPlan));
            Assert.That(written.SubPlanName, Is.EqualTo(subPlanName));
            Assert.NotNull(written.Id);

            // read from db
            List <SubscriptionLog> allItems = await repo.Collection.Find(FilterDefinition <SubscriptionLog> .Empty).ToListAsync();

            Assert.That(allItems.Count, Is.EqualTo(1));
            SubscriptionLog read = allItems[0];

            Assert.That(read, Is.EqualTo(written));

            Assert.That(read.Timestamp, Is.EqualTo(timestamp));
            Assert.That(read.UserId, Is.EqualTo(userId));
            Assert.That(read.MonthsStreak, Is.EqualTo(monthsStreak));
            Assert.That(read.MonthsNumPrev, Is.EqualTo(monthsNumPrev));
            Assert.That(read.MonthsNumNew, Is.EqualTo(monthsNumNew));
            Assert.That(read.MonthsDifference, Is.EqualTo(monthsDifference));
            Assert.That(read.LoyaltyLeaguePrev, Is.EqualTo(loyaltyLeaguePrev));
            Assert.That(read.LoyaltyLeagueNew, Is.EqualTo(loyaltyLeagueNew));
            Assert.That(read.LoyaltyCompletions, Is.EqualTo(loyaltyCompletions));
            Assert.That(read.RewardTokens, Is.EqualTo(rewardTokens));
            Assert.That(read.SubMessage, Is.EqualTo(subMessage));
            Assert.That(read.SubPlan, Is.EqualTo(subPlan));
            Assert.That(read.SubPlanName, Is.EqualTo(subPlanName));
        }
Beispiel #2
0
        public async Task <SubscriptionLog> LogSubscription(
            string userId, Instant timestamp, int monthsStreak, int monthsNumPrev,
            int monthsNumNew, int monthsDifference, int loyaltyLeaguePrev, int loyaltyLeagueNew, int loyaltyCompletions,
            int rewardTokens, string?subMessage, SubscriptionTier subPlan, string subPlanName)
        {
            var item = new SubscriptionLog(
                string.Empty, userId, timestamp,
                monthsStreak, monthsNumPrev, monthsNumNew, monthsDifference,
                loyaltyLeaguePrev, loyaltyLeagueNew, loyaltyCompletions, rewardTokens,
                subMessage, subPlan, subPlanName);
            await Collection.InsertOneAsync(item);

            Debug.Assert(item.Id.Length > 0, "The MongoDB driver injected a generated ID");
            return(item);
        }
 /// <summary>
 /// Disabled Instantly Subscription after sent
 /// </summary>
 /// <param name="model"></param>
 /// <returns></returns>
 public ResponseModel DisabledSubscriptionDirectly(SubscriptionLog model)
 {
     model.IsDirectlySent = true;
     return(Update(model));
 }
 /// <summary>
 /// Deactive Subscription Log
 /// </summary>
 /// <param name="model"></param>
 /// <returns></returns>
 public ResponseModel DeactiveSubscriptionLog(SubscriptionLog model)
 {
     model.Active = false;
     return(Update(model));
 }
 internal ResponseModel Delete(SubscriptionLog subscriptionLog)
 {
     return(_subscriptionLogRepository.Delete(subscriptionLog));
 }
 internal ResponseModel Insert(SubscriptionLog subscriptionLog)
 {
     return(_subscriptionLogRepository.Insert(subscriptionLog));
 }
Beispiel #7
0
 public SubscriptionLogItem(SubscriptionLog log)
     : this()
 {
     ChangeLog  = log.ChangeLog;
     ChangeDate = log.Created;
 }
Beispiel #8
0
        public async Task <ISubscriptionProcessor.SubResult> ProcessSubscription(SubscriptionInfo subscriptionInfo)
        {
            User user = subscriptionInfo.Subscriber;

            if (user.MonthsSubscribed > 0 && user.SubscriptionTier == null)
            {
                _logger.LogWarning("Subscriber {User} has no subscription tier recorded. Assuming Tier 1", user);
                user = await _userRepo.SetSubscriptionInfo(
                    user, user.MonthsSubscribed, SubscriptionTier.Tier1, user.LoyaltyLeague,
                    user.SubscriptionUpdatedAt);
            }

            if (user.MonthsSubscribed == subscriptionInfo.NumMonths &&
                user.SubscriptionTier?.ToRank() >= subscriptionInfo.Tier.ToRank())
            {
                // If twitch reports the new months as being the same as the old months, such as due to repeated message
                // or other error, ignore the sub but send a warning to the user in case of issues.
                return(new ISubscriptionProcessor.SubResult.SameMonth(subscriptionInfo.NumMonths));
            }

            bool subCountCorrected = false;

            if (user.MonthsSubscribed > subscriptionInfo.NumMonths)
            {
                // Repair the user data and re-read it for further processing.
                // Set to current months - 1 to process as a 1-month subscription further down.
                user = await _userRepo.SetSubscriptionInfo(
                    user, subscriptionInfo.NumMonths - 1, subscriptionInfo.Tier, user.LoyaltyLeague,
                    user.SubscriptionUpdatedAt);

                subCountCorrected = true;
            }
            // If our internal months subscribed count is less than what Twitch says it is,
            // we update our count and give the subscriber "back pay" tokens.
            // This can occur when users (re)subscribe while the tpp bot is down.

            Debug.Assert(subscriptionInfo.NumMonths > user.MonthsSubscribed ||
                         (subscriptionInfo.NumMonths == user.MonthsSubscribed &&
                          subscriptionInfo.Tier.ToRank() > user.SubscriptionTier !.Value.ToRank()));

            // Difference between previous recorded subscribed months and new amount.
            int monthsDifference = subscriptionInfo.NumMonths - user.MonthsSubscribed;
            int loyaltyCompletions;

            if (monthsDifference > 0)
            {
                // Additional benefits for expensive plans are given only for the current months to prevent users from
                // tricking us into giving them extra "back pay", e.g. by purposefully not announcing lower grade
                // subscriptions from previous months.
                loyaltyCompletions = monthsDifference - 1 + subscriptionInfo.Tier.ToRank();
            }
            else
            {
                Debug.Assert(user.SubscriptionTier.HasValue,
                             "no months difference must mean it's a tier upgrade, so the user has subscribed before");
                // Same month, but an upgrade in subscription tier. Pay the difference in monetary rank.
                loyaltyCompletions = subscriptionInfo.Tier.ToRank() - user.SubscriptionTier.Value.ToRank();
            }

            const int maxLeague        = 15;
            const int tokensPerLeague  = 2;
            const int baseTokens       = 10;
            int       newLoyaltyLeague = Math.Min(user.LoyaltyLeague + loyaltyCompletions, maxLeague);
            int       tokens           = Enumerable
                                         .Range(user.LoyaltyLeague, loyaltyCompletions)
                                         .Select(league => baseTokens + Math.Min(league, maxLeague) * tokensPerLeague)
                                         .Sum();
            int oldLoyaltyLeague = user.LoyaltyLeague;

            SubscriptionLog subscriptionLog = await _subscriptionLogRepo.LogSubscription(
                user.Id, subscriptionInfo.SubscriptionAt,
                subscriptionInfo.StreakMonths, user.MonthsSubscribed, subscriptionInfo.NumMonths, monthsDifference,
                oldLoyaltyLeague, newLoyaltyLeague, loyaltyCompletions, tokens,
                subscriptionInfo.Message, subscriptionInfo.Tier, subscriptionInfo.PlanName);

            TransactionLog transactionLog = await _tokenBank.PerformTransaction(new Transaction <User>(
                                                                                    user, tokens, TransactionType.Subscription, new Dictionary <string, object?>
            {
                ["previous_months_subscribed"] = user.MonthsSubscribed,
                ["new_months_subscribed"] = subscriptionInfo.NumMonths,
                ["months_difference"] = monthsDifference,
                ["previous_loyalty_tier"] = oldLoyaltyLeague,
                ["new_loyalty_tier"] = newLoyaltyLeague,
                ["loyalty_completions"] = loyaltyCompletions
            }));

            user = await _userRepo.SetIsSubscribed(user, true);

            user = await _userRepo.SetSubscriptionInfo(
                user, subscriptionInfo.NumMonths, subscriptionInfo.Tier, newLoyaltyLeague,
                subscriptionInfo.SubscriptionAt);

            return(new ISubscriptionProcessor.SubResult.Ok(
                       DeltaTokens: tokens,
                       OldLoyaltyLeague: oldLoyaltyLeague,
                       NewLoyaltyLeague: newLoyaltyLeague,
                       CumulativeMonths: subscriptionInfo.NumMonths,
                       SubCountCorrected: subCountCorrected));
        }