2024-06-18 23:54:32 +12:00
#nullable disable
using LinqToDB ;
using LinqToDB.EntityFrameworkCore ;
2024-06-27 20:22:54 +12:00
using EllieBot.Common.ModuleBehaviors ;
2024-06-18 23:54:32 +12:00
using EllieBot.Modules.Gambling.Services ;
using EllieBot.Modules.Patronage ;
using EllieBot.Services.Currency ;
using EllieBot.Db.Models ;
namespace EllieBot.Modules.Utility ;
2024-06-27 20:22:54 +12:00
public sealed class CurrencyRewardService : IEService , IReadyExecutor
2024-06-18 23:54:32 +12:00
{
private readonly ICurrencyService _cs ;
private readonly IPatronageService _ps ;
private readonly DbService _db ;
private readonly IMessageSenderService _sender ;
private readonly GamblingConfigService _config ;
private readonly DiscordSocketClient _client ;
public CurrencyRewardService (
ICurrencyService cs ,
IPatronageService ps ,
DbService db ,
IMessageSenderService sender ,
GamblingConfigService config ,
DiscordSocketClient client )
{
_cs = cs ;
_ps = ps ;
_db = db ;
_sender = sender ;
_config = config ;
_client = client ;
2024-06-27 20:22:54 +12:00
}
public Task OnReadyAsync ( )
{
2024-06-18 23:54:32 +12:00
_ps . OnNewPatronPayment + = OnNewPayment ;
_ps . OnPatronRefunded + = OnPatronRefund ;
_ps . OnPatronUpdated + = OnPatronUpdate ;
2024-06-27 20:22:54 +12:00
return Task . CompletedTask ;
2024-06-18 23:54:32 +12:00
}
private async Task OnPatronUpdate ( Patron oldPatron , Patron newPatron )
{
// if pledge was increased
if ( oldPatron . Amount < newPatron . Amount )
{
var conf = _config . Data ;
var newAmount = ( long ) ( newPatron . Amount * conf . PatreonCurrencyPerCent ) ;
RewardedUser old ;
await using ( var ctx = _db . GetDbContext ( ) )
{
old = await ctx . GetTable < RewardedUser > ( )
. Where ( x = > x . PlatformUserId = = newPatron . UniquePlatformUserId )
. FirstOrDefaultAsync ( ) ;
2024-06-27 20:22:54 +12:00
2024-06-18 23:54:32 +12:00
if ( old is null )
{
await OnNewPayment ( newPatron ) ;
return ;
}
2024-06-27 20:22:54 +12:00
2024-06-18 23:54:32 +12:00
// no action as the amount is the same or lower
if ( old . AmountRewardedThisMonth > = newAmount )
return ;
2024-06-27 20:22:54 +12:00
2024-06-18 23:54:32 +12:00
var count = await ctx . GetTable < RewardedUser > ( )
. Where ( x = > x . PlatformUserId = = newPatron . UniquePlatformUserId )
. UpdateAsync ( _ = > new ( )
{
PlatformUserId = newPatron . UniquePlatformUserId ,
UserId = newPatron . UserId ,
// amount before bonuses
AmountRewardedThisMonth = newAmount ,
LastReward = newPatron . PaidAt
} ) ;
// shouldn't ever happen
if ( count = = 0 )
return ;
}
var oldAmount = old . AmountRewardedThisMonth ;
var realNewAmount = GetRealCurrencyReward (
( int ) ( newAmount / conf . PatreonCurrencyPerCent ) ,
newAmount ,
out var percentBonus ) ;
2024-06-27 20:22:54 +12:00
2024-06-18 23:54:32 +12:00
var realOldAmount = GetRealCurrencyReward (
( int ) ( oldAmount / conf . PatreonCurrencyPerCent ) ,
oldAmount ,
out _ ) ;
2024-06-27 20:22:54 +12:00
2024-06-18 23:54:32 +12:00
var diff = realNewAmount - realOldAmount ;
if ( diff < = 0 )
return ; // no action if new is lower
// if the user pledges 5$ or more, they will get X % more flowers where X is amount in dollars,
// up to 100%
2024-06-27 20:22:54 +12:00
await _cs . AddAsync ( newPatron . UserId , diff , new TxData ( "patron" , "update" ) ) ;
2024-06-18 23:54:32 +12:00
_ = SendMessageToUser ( newPatron . UserId ,
$"You've received an additional **{diff}**{_config.Data.Currency.Sign} as a currency reward (+{percentBonus}%)!" ) ;
}
}
private long GetRealCurrencyReward ( int pledgeCents , long modifiedAmount , out int percentBonus )
{
// needs at least 5$ to be eligible for a bonus
if ( pledgeCents < 500 )
{
percentBonus = 0 ;
return modifiedAmount ;
}
var dollarValue = pledgeCents / 100 ;
percentBonus = dollarValue switch
{
> = 100 = > 100 ,
> = 50 = > 50 ,
> = 20 = > 20 ,
> = 10 = > 10 ,
> = 5 = > 5 ,
_ = > 0
} ;
return ( long ) ( modifiedAmount * ( 1 + ( percentBonus / 100.0f ) ) ) ;
}
// on a new payment, always give the full amount.
private async Task OnNewPayment ( Patron patron )
{
var amount = ( long ) ( patron . Amount * _config . Data . PatreonCurrencyPerCent ) ;
await using var ctx = _db . GetDbContext ( ) ;
await ctx . GetTable < RewardedUser > ( )
. InsertOrUpdateAsync ( ( ) = > new ( )
2024-06-27 20:22:54 +12:00
{
PlatformUserId = patron . UniquePlatformUserId ,
UserId = patron . UserId ,
AmountRewardedThisMonth = amount ,
LastReward = patron . PaidAt ,
} ,
2024-06-18 23:54:32 +12:00
old = > new ( )
{
AmountRewardedThisMonth = amount ,
UserId = patron . UserId ,
LastReward = patron . PaidAt
} ,
( ) = > new ( )
{
PlatformUserId = patron . UniquePlatformUserId
} ) ;
2024-06-27 20:22:54 +12:00
2024-06-18 23:54:32 +12:00
var realAmount = GetRealCurrencyReward ( patron . Amount , amount , out var percentBonus ) ;
await _cs . AddAsync ( patron . UserId , realAmount , new ( "patron" , "new" ) ) ;
_ = SendMessageToUser ( patron . UserId ,
$"You've received **{realAmount}**{_config.Data.Currency.Sign} as a currency reward (**+{percentBonus}%**)!" ) ;
}
private async Task SendMessageToUser ( ulong userId , string message )
{
try
{
var user = ( IUser ) _client . GetUser ( userId ) ? ? await _client . Rest . GetUserAsync ( userId ) ;
if ( user is null )
return ;
var eb = _sender . CreateEmbed ( )
. WithOkColor ( )
. WithDescription ( message ) ;
2024-06-27 20:22:54 +12:00
2024-06-18 23:54:32 +12:00
await _sender . Response ( user ) . Embed ( eb ) . SendAsync ( ) ;
}
catch
{
Log . Warning ( "Unable to send a \"Currency Reward\" message to the patron {UserId}" , userId ) ;
}
}
private async Task OnPatronRefund ( Patron patron )
{
await using var ctx = _db . GetDbContext ( ) ;
_ = await ctx . GetTable < RewardedUser > ( )
. UpdateAsync ( old = > new ( )
{
AmountRewardedThisMonth = old . AmountRewardedThisMonth * 2
} ) ;
}
}